/**
 * @file  gca.c
 * @brief Core routines implementing the Gaussian Classifier Atlas mechanism
 *
 * Routines implementing the mechanism of encoding voxel label information
 * based on probabilistic information estimated from a training set.
 *
 * Reference:
 * "Whole Brain Segmentation: Automated Labeling of Neuroanatomical
 * Structures in the Human Brain", Fischl et al.
 * (2002) Neuron, 33:341-355.
 */
/*
 * Original Author: Bruce Fischl
 * CVS Revision Info:
 *    $Author: fischl $
 *    $Date: 2007/05/11 20:20:05 $
 *    $Revision: 1.230 $
 *
 * Copyright (C) 2002-2007,
 * The General Hospital Corporation (Boston, MA). 
 * All rights reserved.
 *
 * Distribution, usage and copying of this software is covered under the
 * terms found in the License Agreement file named 'COPYING' found in the
 * FreeSurfer source code root directory, and duplicated here:
 * https://surfer.nmr.mgh.harvard.edu/fswiki/FreeSurferOpenSourceLicense
 *
 * General inquiries: freesurfer@nmr.mgh.harvard.edu
 * Bug reports: analysis-bugs@nmr.mgh.harvard.edu
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>

#include "mri.h"
#include "error.h"
#include "diag.h"
#include "utils.h"
#include "gca.h"
#include "proto.h"
#include "fio.h"
#include "transform.h"
#include "macros.h"
#include "utils.h"
#include "cma.h"
#include "flash.h"
#include "talairachex.h"
#include "mrimorph.h"
#include "intensity_eig.h"
#include "numerics.h"
#include "mrisegment.h"

#if WITH_DMALLOC
#include <dmalloc.h>
#endif

int Ggca_label = -1 ;
int Ggca_nbr_label = -1 ;
int Ggca_x = -1 ;
int Ggca_y = -1 ;
int Ggca_z = -1 ;
int Gxp = -1;
int Gyp = -1;
int Gzp = -1;
int Gxn = -1; // 32;
int Gyn = -1; // 21;
int Gzn = -1; // 32;
char *G_write_probs = NULL ;

/* this is the hack section */
double PRIOR_FACTOR = 0.1 ;
#define LABEL_UNDETERMINED   255

static int total_pruned = 0 ;

#define MIN_DET 1e-7
/* less than this, and the covariance matrix is poorly conditioned */

#define MAX_LABELS_PER_GCAN         25
#define MAX_DIFFERENT_LABELS        500
#define MIN_VAR                     (5*5)   /* should make this configurable */
#define BIG_AND_NEGATIVE            -10000000.0
#define VERY_UNLIKELY               1e-10
#define UNKNOWN_DIST                4  /* within 4 mm of some known label */
#define GCA_OLD_VERSION             2.0
#define GCA_UCHAR_VERSION           4.0  // labels were uchars in file
#define GCA_INT_VERSION             5.0  // labels are ints in file
#define DEFAULT_MAX_LABELS_PER_GCAN 4

//static int gcapBrainIsPossible(GCA_PRIOR *gcap) ;
#if 0
static HISTOGRAM *gcaComputeHistogramNormalization(GCA *gca,
    HISTOGRAM *h_mri,
    int label) ;
static float gcaFindCerebellarScaleFactor(GCA *gca,
    HISTOGRAM *h_mri,
    int label,
    FILE *logfp);
static double GCAcomputeScaledMeanEntropy(GCA *gca, 
                                          MRI *mri, 
                                          TRANSFORM *transform,
    int *labels, float *scales, int nlabels) ;
#endif
static int gcaScale(GCA *gca, int *labels, int *contra_labels, 
                    float *scales, int nlabels, int dir);
#if 0
static int gcaMaxPriorLabel(GCA *gca, 
                            MRI *mri, 
                            TRANSFORM *transform, 
                            int x, int y, int z) ;
#endif
static HISTOGRAM *gcaGetLabelHistogram(GCA *gca, int label, int frame) ;
int GCAmaxLabel(GCA *gca) ;
static int gcaRelabelSegment(GCA *gca,
                             TRANSFORM *transform,
                             MRI *mri_inputs,
                             MRI *mri_dst,
                             MRI_SEGMENT *mseg) ;

#define INTERP_PRIOR 0
#if INTERP_PRIOR
static float gcaComputePrior(GCA *gca, MRI *mri, TRANSFORM *transform,
                             int x, int y, int z, int label) ;
#endif

static int gcapGetMaxPriorLabel(GCA_PRIOR *gcap, double *p_prior) ;
double compute_partial_volume_log_posterior(GCA *gca,
    GCA_NODE *gcan,
    GCA_PRIOR *gcap,
    float *vals, int l1, int l2) ;
static double gcaComputeSampleConditionalLogDensity(GCA_SAMPLE *gcas,
    float *vals,
    int ninputs,
    int label) ;
static VECTOR *load_sample_mean_vector(GCA_SAMPLE *gcas,
                                       VECTOR *v_means,
                                       int ninputs) ;
static MATRIX *load_sample_covariance_matrix(GCA_SAMPLE *gcas,
    MATRIX *m_cov,
    int ninputs) ;
static double sample_covariance_determinant(GCA_SAMPLE *gcas, int ninputs) ;
static GC1D *findClosestValidGC(GCA *gca,
                                int x, int y, int z,
                                int label, int check_var) ;
static GC1D *findGCInWindow(GCA *gca,
                            int x, int y, int z,
                            int label, int wsize) ;

static int    MRIorderIndices(MRI *mri, short *x_indices, short *y_indices,
                              short *z_indices) ;
static int gcaCheck(GCA *gca) ;
static double gcaVoxelLogLikelihood(GCA *gca,
                                    MRI *mri_labels,
                                    MRI *mri_inputs,
                                    int x, int y, int z,
                                    TRANSFORM *transform);
static double gcaVoxelGibbsLogLikelihood(GCA *gca,
    MRI *mri_labels,
    MRI *mri_inputs,
    int x, int y, int z,
    TRANSFORM *transform,
    double gibbs_coef) ;
static double gcaNbhdGibbsLogLikelihood(GCA *gca,
                                        MRI *mri_labels,
                                        MRI *mri_inputs,
                                        int x, int y, int z,
                                        TRANSFORM *transform,
                                        double gibbs_coef) ;
static double gcaGibbsImpossibleConfiguration(GCA *gca,
    MRI *mri_labels,
    int x, int y, int z,
    TRANSFORM *transform) ;
static GCA_SAMPLE *gcaExtractLabelAsSamples(GCA *gca,
    MRI *mri_labeled,
    TRANSFORM *transform,
    int *pnsamples, int label) ;
#if 0
static GCA_SAMPLE *gcaExtractRegionLabelAsSamples(GCA *gca, MRI *mri_labeled,
    TRANSFORM *transform,
    int *pnsamples, int label,
    int xn, int yn, int zn,
    int wsize) ;
#endif
static GCA_SAMPLE *gcaExtractThresholdedRegionLabelAsSamples
(GCA *gca,
 MRI *mri_labeled,
 TRANSFORM *transform,
 int *pnsamples,
 int label,
 int xn,
 int yn,
 int zn,
 int wsize,
 float pthresh);
/*static double gcaComputeSampleConditionalDensity(GCA_SAMPLE *gcas,
  float *vals, int ninputs, int label) ;*/
static double gcaComputeLogDensity(GC1D *gc, float *vals, \
                                   int ninputs, float prior, int label) ;
static double gcaComputeSampleLogDensity(GCA_SAMPLE *gcas,
    float *vals, int ninputs) ;
int MRIcomputeVoxelPermutation(MRI *mri, short *x_indices, short *y_indices,
                               short *z_indices);
GCA *gcaAllocMax(int ninputs, float prior_spacing, float node_spacing, \
                 int width, int height, int depth,
                 int max_labels, int flags) ;
static int GCAupdateNode(GCA *gca, MRI *mri, int xn, int yn, int zn,
                         float *vals,int label, GCA *gca_prune, int noint);
static int GCAupdateNodeCovariance(GCA *gca, MRI *mri, int xn, int yn, int zn,
                                   float *vals,int label);
static int GCAupdatePrior(GCA *gca,
                          MRI *mri,
                          int xn, int yn, int zn,
                          int label);
static int GCAupdateNodeGibbsPriors(GCA *gca,MRI*mri,int xn,int yn,int zn,
                                    int x, int y, int z, int label);
#if 0
static int different_nbr_labels(GCA *gca, int x, int y, int z, int wsize,
                                int label, float pthresh) ;
#endif
static int different_nbr_max_labels(GCA *gca, int x, int y, int z, int wsize,
                                    int label) ;
static int gcaRegionStats(GCA *gca, int x0, int y0, int z0, int wsize,
                          float *priors, float *vars, \
                          float means[MAX_DIFFERENT_LABELS][MAX_GCA_INPUTS]) ;
static int gcaFindBestSample(GCA *gca, int x, int y, int z, int best_label,
                             int wsize, GCA_SAMPLE *gcas);
#if 0
static int gcaFindClosestMeanSample(GCA *gca, float *means, float min_prior,
                                    int x, int y, int z,
                                    int label, int wsize, GCA_SAMPLE *gcas);
static GC1D *gcaFindHighestPriorGC(GCA *gca, int x, int y, int z,int label,
                                   int wsize) ;
#endif
static double gcaGibbsImageLogLikelihood(GCA *gca,
    MRI *mri_labels,
    MRI *mri_inputs,
    TRANSFORM *transform) ;

static int mriFillRegion(MRI *mri, int x,int y,int z,int fill_val,int whalf);
static int gcaFindMaxPriors(GCA *gca, float *max_priors) ;
static int gcaFindIntensityBounds(GCA *gca, float *pmin, float *pmax) ;
static int dump_gcan(GCA *gca,
                     GCA_NODE *gcan,
                     FILE *fp,
                     int verbose,
                     GCA_PRIOR *gcap) ;
static GCA_NODE *findSourceGCAN(GCA *gca,
                                MRI *mri_src,
                                TRANSFORM *transform,
                                int x,int y,int z);
#if 0
static int getLabelMean(GCA_NODE *gcan, int label, \
                        float *pvar, float *means, int ninputs) ;
#endif
static int   borderVoxel(MRI *mri, int x, int y, int z) ;
static int   GCAmaxLikelihoodBorderLabel(GCA *gca, MRI *mri_inputs,
    MRI *mri_labels, TRANSFORM *transform,
    int x, int y, int z, float min_ratio);


float getPrior(GCA_PRIOR *gcap, int label) ;
GCA_PRIOR *getGCAP(GCA *gca,
                   MRI *mri,
                   TRANSFORM *transform,
                   int xv, int yv, int zv) ;
GCA_PRIOR *getGCAPfloat(GCA *gca,
                        MRI *mri,
                        TRANSFORM *transform,
                        float xv, float yv, float zv) ;
GCA_NODE *getGCAN(GCA *gca,
                  MRI *mri,
                  TRANSFORM *transform,
                  int xv, int yv, int zv) ;
static int gcaNodeToPrior(GCA *gca,
                          int xn, int yn, int zn,
                          int *pxp, int *pyp, int *pzp) ;
static HISTOGRAM *gcaHistogramSamples(GCA *gca, GCA_SAMPLE *gcas, MRI *mri,
                                      TRANSFORM *transform, int nsamples,
                                      HISTOGRAM *histo, int frame) ;
int GCApriorToNode(GCA *gca,
                   int xp, int yp, int zp,
                   int *pxn, int *pyn, int *pzn) ;

/* arrays for indexing 6-connected neighbors */
static int xnbr_offset[] =
  {
    1, -1, 0, 0,  0,  0
  } ;
static int ynbr_offset[] =
  {
    0, 0,  1, -1, 0,  0
  } ;
static int znbr_offset[] =
  {
    0, 0,  0, 0,  1, -1
  } ;
int check_finite(char *where, double what) ;
static int boundsCheck(int *pix, int *piy, int *piz, MRI *mri);

static void  set_equilavent_classes(int *equivalent_classes);

static int initialize_ventricle_alignment(MRI *mri_seg, 
                                          MRI *mri, 
                                          MATRIX *m_L, 
                                          char *base_name) ;


void GCAsetVolGeom(GCA *gca, VOL_GEOM *vg)
{
  vg->width = gca->width;
  vg->height = gca->height;
  vg->depth = gca->depth;
  vg->xsize = gca->xsize;
  vg->ysize = gca->ysize;
  vg->zsize = gca->zsize;
  vg->x_r = gca->x_r;
  vg->y_r = gca->y_r;
  vg->z_r = gca->z_r;
  vg->x_a = gca->x_a;
  vg->y_a = gca->y_a;
  vg->z_a = gca->z_a;
  vg->x_s = gca->x_s;
  vg->y_s = gca->y_s;
  vg->z_s = gca->z_s;
  vg->c_r = gca->c_r;
  vg->c_a = gca->c_a;
  vg->c_s = gca->c_s;
  vg->valid = 1;
}

void GCAcopyDCToMRI(GCA *gca, MRI *mri)
{
  mri->x_r = gca->x_r;
  mri->y_r = gca->y_r;
  mri->z_r = gca->z_r;
  mri->x_a = gca->x_a;
  mri->y_a = gca->y_a;
  mri->z_a = gca->z_a;
  mri->x_s = gca->x_s;
  mri->y_s = gca->y_s;
  mri->z_s = gca->z_s;
  mri->c_r = gca->c_r;
  mri->c_a = gca->c_a;
  mri->c_s = gca->c_s;
  mri->ras_good_flag = 1;
	MRIreInitCache(mri) ;
  mri->i_to_r__ = extract_i_to_r(mri);
  mri->r_to_i__ = extract_r_to_i(mri);
}
void GCAcopyDCToGCA(GCA *gca, GCA *gca_dst)
{
  gca_dst->x_r = gca->x_r;
  gca_dst->y_r = gca->y_r;
  gca_dst->z_r = gca->z_r;
  gca_dst->x_a = gca->x_a;
  gca_dst->y_a = gca->y_a;
  gca_dst->z_a = gca->z_a;
  gca_dst->x_s = gca->x_s;
  gca_dst->y_s = gca->y_s;
  gca_dst->z_s = gca->z_s;
  gca_dst->c_r = gca->c_r;
  gca_dst->c_a = gca->c_a;
  gca_dst->c_s = gca->c_s;
  //
  gca_dst->node_i_to_r__ =
    MatrixCopy(gca->node_i_to_r__, gca_dst->node_i_to_r__);
  gca_dst->node_r_to_i__ =
    MatrixCopy(gca->node_r_to_i__, gca_dst->node_r_to_i__);
  gca_dst->prior_i_to_r__ =
    MatrixCopy(gca->prior_i_to_r__, gca_dst->prior_i_to_r__);
  gca_dst->prior_r_to_i__ =
    MatrixCopy(gca->prior_r_to_i__, gca_dst->prior_r_to_i__);
  gca_dst->tal_i_to_r__ = MatrixCopy(gca->tal_i_to_r__, gca_dst->tal_i_to_r__);
  gca_dst->tal_r_to_i__ = MatrixCopy(gca->tal_r_to_i__, gca_dst->tal_r_to_i__);
  //
  gca_dst->mri_prior__ = MRIcopy(gca->mri_prior__, gca_dst->mri_prior__);
  gca_dst->mri_node__ = MRIcopy(gca->mri_node__, gca_dst->mri_node__);
  gca_dst->mri_tal__ = MRIcopy(gca->mri_tal__, gca_dst->mri_tal__);
}

void GCAcleanup(GCA *gca)
{
  if (gca->mri_node__)
  {
    MRIfree(&gca->mri_node__);
    gca->mri_node__= 0;
  }
  if (gca->mri_prior__)
  {
    MRIfree(&gca->mri_prior__);
    gca->mri_prior__ = 0;
  }
  if (gca->mri_tal__)
  {
    MRIfree(&gca->mri_tal__);
    gca->mri_tal__ = 0;
  }
  if (gca->node_i_to_r__)
  {
    MatrixFree(&(gca->node_i_to_r__));
    gca->node_i_to_r__ = 0;
  }
  if (gca->node_r_to_i__)
  {
    MatrixFree(&(gca->node_r_to_i__));
    gca->node_r_to_i__ = 0;
  }
  if (gca->prior_i_to_r__)
  {
    MatrixFree(&(gca->prior_i_to_r__));
    gca->prior_i_to_r__ = 0;
  }
  if (gca->prior_r_to_i__)
  {
    MatrixFree(&(gca->prior_r_to_i__));
    gca->prior_r_to_i__ = 0;
  }
  if (gca->tal_i_to_r__)
  {
    MatrixFree(&(gca->tal_i_to_r__));
    gca->tal_i_to_r__ = 0;
  }
  if (gca->tal_r_to_i__)
  {
    MatrixFree(&(gca->tal_r_to_i__));
    gca->tal_r_to_i__ = 0;
  }
  if (gca->tmp__)
  {
    MatrixFree(&gca->tmp__);
    gca->tmp__ = 0;
  }
}

// set up mri's used in GCA
void GCAsetup(GCA *gca)
{
  // set up node part /////////////////////
  if (gca->mri_node__)
  {
    MRIfree(&gca->mri_node__);
    gca->mri_node__ = 0;
  }
  gca->mri_node__ = \
                    MRIallocHeader(gca->node_width,
                                   gca->node_height,
                                   gca->node_depth,
                                   MRI_UCHAR);
  /* Copy the voxel resolutions.  Set the defaults */
  gca->mri_node__->xsize = gca->xsize * gca->node_spacing;
  gca->mri_node__->ysize = gca->ysize * gca->node_spacing;
  gca->mri_node__->zsize = gca->zsize * gca->node_spacing;

  // this will recalculate i_to_r__ etc and
  // thus xsize etc must be set correctly
  GCAcopyDCToMRI(gca, gca->mri_node__);

  // fprintf(stdout, "node voxelToRAS\n");
  // MATRIX *mnode = extract_i_to_r(gca->mri_node__);
  // MatrixPrint(stdout, mnode);
  // MatrixFree(&mnode);

  // setup prior part //////////////////////////////////////
  if (gca->mri_prior__)
  {
    MRIfree(&gca->mri_prior__);
    gca->mri_prior__ = 0;
  }
  gca->mri_prior__ =
    MRIallocHeader(gca->prior_width,
                   gca->prior_height,
                   gca->prior_depth,
                   MRI_UCHAR);

  /* Copy the voxel resolutions.  Set the defaults */
  gca->mri_prior__->xsize = gca->xsize * gca->prior_spacing;
  gca->mri_prior__->ysize = gca->ysize * gca->prior_spacing;
  gca->mri_prior__->zsize = gca->zsize * gca->prior_spacing;

  GCAcopyDCToMRI(gca, gca->mri_prior__);

  // fprintf(stdout, "prior voxelToRAS\n");
  // MATRIX *mprior = extract_i_to_r(gca->mri_prior__);
  // MatrixPrint(stdout, mprior);
  // MatrixFree(&mprior);

  // set up the default talairach volume ////////////////
  if (gca->mri_tal__)
  {
    MRIfree(&gca->mri_tal__);
    gca->mri_tal__ = 0;
  }
  gca->mri_tal__ = 
    MRIallocHeader(gca->width, gca->height, gca->depth, MRI_UCHAR);

  /* Copy the voxel resolutions.  Set the defaults */
  gca->mri_tal__->xsize = gca->xsize;
  gca->mri_tal__->ysize = gca->ysize;
  gca->mri_tal__->zsize = gca->zsize;

  GCAcopyDCToMRI(gca, gca->mri_tal__);

  //fprintf(stdout, "tal voxelToRAS\n");
  //mtal = extract_i_to_r(gca->mri_tal__);
  // MatrixPrint(stdout, mtal);
  // MatrixFree(&mtal);
  if (gca->node_i_to_r__)
  {
    MatrixFree(&(gca->node_i_to_r__));
    gca->node_i_to_r__ = 0;
  }
  if (gca->node_r_to_i__)
  {
    MatrixFree(&(gca->node_r_to_i__));
    gca->node_r_to_i__ = 0;
  }
  if (gca->prior_i_to_r__)
  {
    MatrixFree(&(gca->prior_i_to_r__));
    gca->prior_i_to_r__ = 0;
  }
  if (gca->prior_r_to_i__)
  {
    MatrixFree(&(gca->prior_r_to_i__));
    gca->prior_r_to_i__ = 0;
  }
  if (gca->tal_i_to_r__)
  {
    MatrixFree(&(gca->tal_i_to_r__));
    gca->tal_i_to_r__ = 0;
  }
  if (gca->tal_r_to_i__)
  {
    MatrixFree(&(gca->tal_r_to_i__));
    gca->tal_r_to_i__ = 0;
  }
  if (gca->tmp__)
  {
    MatrixFree(&(gca->tmp__));
    gca->tmp__ = 0;
  }
  gca->node_i_to_r__ = extract_i_to_r(gca->mri_node__);
  gca->node_r_to_i__ = extract_r_to_i(gca->mri_node__);
  gca->prior_i_to_r__ = extract_i_to_r(gca->mri_prior__);
  gca->prior_r_to_i__ = extract_r_to_i(gca->mri_prior__);
  gca->tal_i_to_r__ = extract_i_to_r(gca->mri_tal__);
  gca->tal_r_to_i__ = extract_r_to_i(gca->mri_tal__);
  gca->tmp__ = MatrixAlloc(4,4, MATRIX_REAL);
}

// using the values of mri, modify gca
// now we can keep track of voxelToRAS info even for non-standard type
// This has the potential of changing direction cosines
void GCAreinit(MRI *mri, GCA *gca)
{
  // Keep the direction cosine the same to avoid used by
  // different mri
  // modify direction cosines etc.
  gca->x_r = mri->x_r; gca->y_r = mri->y_r; gca->z_r = mri->z_r;
  gca->x_a = mri->x_a; gca->y_a = mri->y_a; gca->z_a = mri->z_a;
  gca->x_s = mri->x_s; gca->y_s = mri->y_s; gca->z_s = mri->z_s;
  gca->c_r = mri->c_r; gca->c_a = mri->c_a; gca->c_s = mri->c_s;

  if (Gdiag & DIAG_SHOW && DIAG_VERBOSE_ON)
    printf("gca reinit c_(ras) = (%.2f, %.2f, %.2f)\n",
           gca->c_r, gca->c_a, gca->c_s);

  // modify width height depth
  if (gca->width != mri->width)
    fprintf(stdout, "gca width modified from %d to %d\n",
            gca->width, mri->width);
  if (gca->height != mri->height)
    fprintf(stdout, "gca height modified from %d to %d\n",
            gca->height, mri->height);
  if (gca->depth != mri->depth)
    fprintf(stdout, "gca depth modified from %d to %d\n",
            gca->depth, mri->depth);
  gca->width = mri->width;
  gca->height = mri->height;
  gca->depth = mri->depth;

  if (gca->xsize != mri->xsize)
    fprintf(stdout, "gca xsize modified from %.3f to %.3f\n",
            gca->xsize, mri->xsize);
  if (gca->ysize != mri->ysize)
    fprintf(stdout, "gca ysize modified from %.3f to %.3f\n",
            gca->ysize, mri->ysize);
  if (gca->zsize != mri->zsize)
    fprintf(stdout, "gca zsize modified from %.3f to %.3f\n",
            gca->zsize, mri->zsize);
  gca->xsize = mri->xsize;
  gca->ysize = mri->ysize;
  gca->zsize = mri->zsize;
#if 0
	// can't do this without reallocating!!
  // then must modify node width etc.
  gca->node_width = (int)(((float)gca->width/gca->node_spacing)+.99) ;
  gca->node_height = (int)((float)gca->height/gca->node_spacing+.99) ;
  gca->node_depth = (int)(((float)gca->depth/gca->node_spacing)+.99) ;
  gca->prior_width = (int)(((float)gca->width/gca->prior_spacing)+.99) ;
  gca->prior_height = (int)((float)gca->height/gca->prior_spacing+.99) ;
  gca->prior_depth = (int)(((float)gca->depth/gca->prior_spacing)+.99) ;
#endif
  //
  GCAsetup(gca);
  fflush(stdout) ;
}

GCA_PRIOR *
getGCAP(GCA *gca, MRI *mri, TRANSFORM *transform, int xv, int yv, int zv)
{
  int       xp, yp, zp ;
  GCA_PRIOR *gcap=NULL;

  if (!GCAsourceVoxelToPrior(gca, mri, transform, xv, yv, zv, &xp, &yp, &zp))
    gcap = &gca->priors[xp][yp][zp] ;

  return(gcap) ;
}

GCA_PRIOR *
getGCAPfloat(GCA *gca, MRI *mri, TRANSFORM *transform, float xv, float yv, float zv)
{
  int       xp, yp, zp ;
  GCA_PRIOR *gcap=NULL;

  if (!GCAsourceFloatVoxelToPrior(gca, mri, transform, xv, yv, zv, &xp, &yp, &zp))
    gcap = &gca->priors[xp][yp][zp] ;

  return(gcap) ;
}

GCA_NODE *
getGCAN(GCA *gca, MRI *mri, TRANSFORM *transform, int xv, int yv, int zv)
{
  int       xn, yn, zn ;
  GCA_NODE *gcan=NULL;

  if (!GCAsourceVoxelToNode(gca, mri, transform, xv, yv, zv, &xn, &yn, &zn))
    gcan = &gca->nodes[xn][yn][zn] ;

  return(gcan) ;
}

float
getPrior(GCA_PRIOR *gcap, int label)
{
  int n ;

  if (gcap==NULL)
    return (VERY_UNLIKELY);

  // find the label
  for (n = 0 ; n < gcap->nlabels ; n++)
    if (gcap->labels[n] == label)
      break ;
  // cannot find it
  if (n >= gcap->nlabels)
  {
    if (gcap->total_training > 0)
      return(0.1f/(float)gcap->total_training) ; /* make it unlikely */
    else
      return(VERY_UNLIKELY) ;
  }
  // return found one
  return(gcap->priors[n]) ;
}

int
GCAisPossible(GCA *gca, MRI *mri, int label,
              TRANSFORM *transform, int x, int y, int z,
              int use_mrf)
{
  int       n, i, found, nbr_label, xnbr, ynbr, znbr, xn, yn, zn ;
  GCA_PRIOR *gcap = NULL;
  GC1D      *gc ;

  gcap = getGCAP(gca, mri, transform, x, y, z) ;
  if (gcap==NULL)
    return(0) ;
  // if label found
  for (found = n = 0 ; n < gcap->nlabels ; n++)
    if (gcap->labels[n] == label)
    {
      found = 1 ;
      break ;
    }
  if (found == 0)
    return(0) ;
  if (use_mrf == 0)
    return(1) ;

  if (GCAsourceVoxelToNode(gca, mri, transform, x, y, z, &xn, &yn, &zn)
      != NO_ERROR)
    return(1) ;
  gc = GCAfindGC(gca, xn, yn, zn, label) ;
  for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
  {
    found = 0 ;
    xnbr = mri->xi[x+xnbr_offset[i]] ;// xnbr_offset 1, -1, 0,  0, 0,  0
    ynbr = mri->yi[y+ynbr_offset[i]] ;// ynbr_offset 0,  0, 1, -1, 0,  0
    znbr = mri->zi[z+znbr_offset[i]] ;// znbr_offset 0,  0, 0,  0, 1, -1
    nbr_label = nint(MRIgetVoxVal(mri, xnbr, ynbr, znbr, 0)) ;
    for (n = 0 ; n < gc->nlabels[i] ; n++)
      if (gc->labels[i][n] == nbr_label)
      {
        found = 1 ;
        break ;
      }
    if (found == 0)  // this pair never occurred here
      return(0) ;
  }
  return(1) ;
}

#if 0
static float get_voxel_prior(GCA *gca, MRI *mri, TRANSFORM *transform,
                             int xv, int yv, int zv, int label);
static float
get_voxel_prior(GCA *gca, MRI *mri, TRANSFORM *transform,
                int xv, int yv, int zv, int label)
{
  int       xn, yn, zn ;
  GCA_PRIOR *gcap ;

  gcap = getGCAP(gca, mri, transform, xv, yv, zv) ;
  // this could return NULL
  if (gcap==NULL)
    return (VERY_UNLIKELY)

           return(getPrior(gcap, label)) ;
}
#endif

static float
get_node_prior(GCA *gca, int label, int xn, int yn, int zn)
{
  int       xp, yp, zp ;
  GCA_PRIOR *gcap ;

  if (gcaNodeToPrior(gca, xn, yn, zn, &xp, &yp, &zp)==NO_ERROR)
  {
    gcap = &gca->priors[xp][yp][zp] ;
    if (gcap == NULL)
      return (VERY_UNLIKELY);
    return(getPrior(gcap, label)) ;
  }
  else
    return (VERY_UNLIKELY);
}

// use the same function for bounds checking
// if (ix, iy, iz) is within mri volume, returns NO_ERROR
//                                       otherwise ERROR_BADPARAM
static int boundsCheck(int *pix, int *piy, int *piz, MRI *mri)
{
  int ix = *pix;
  int iy = *piy;
  int iz = *piz;
  int errCode = NO_ERROR; // initialize

  if (ix < 0)
    errCode = ERROR_BADPARM;
  else if (iy < 0)
    errCode = ERROR_BADPARM;
  else if (iz < 0)
    errCode = ERROR_BADPARM;
  else if (ix > mri->width-1)
    errCode = ERROR_BADPARM;
  else if (iy > mri->height-1)
    errCode = ERROR_BADPARM;
  else if (iz > mri->depth-1)
    errCode = ERROR_BADPARM;

#if 0
  // give up returning error
  if (ix < 0)
    *pix = 0;
  if (iy < 0)
    *piy = 0;
  if (iz < 0)
    *piz = 0;
  if (ix > mri->width -1)
    *pix = mri->width -1;
  if (iy > mri->height-1)
    *piy = mri->height -1;
  if (iz > mri->depth-1)
    *piz = mri->depth -1;
#endif

  return errCode;
}

static int
gcaNodeToPrior(GCA *gca, int xn, int yn, int zn, int *pxp, int *pyp, int *pzp)
{
  // initialize errCode
  int errCode = NO_ERROR;

  // see GCApriorToNode comment
  // node_spacing > prior_spacing
  // integer operation is floor
  *pxp = xn*gca->node_spacing/gca->prior_spacing;
  *pyp = yn*gca->node_spacing/gca->prior_spacing;
  *pzp = zn*gca->node_spacing/gca->prior_spacing;
  // no error should occur
  return errCode ;
}

int
GCApriorToNode(GCA *gca, int xp, int yp, int zp, int *pxn, int *pyn, int *pzn)
{
  int errCode = NO_ERROR;
  /////////////////////////////////////////////////////////////////////
  // Note that prior and node share the common direction cosines/translation
  //
  //      prior has voxelToRAS = [ R | T ][prior_size | 0] = X [prior_size | 0]
  //                             [ 0 | 1 ][    0      | 1]     [    0      | 1]
  //      node  has voxelToRAS = [ R | T ][ node_size | 0] = X [ node_size | 0]
  //                             [ 0 | 1 ][    0      | 1]     [    0      | 1]
  //
  //        prior   --->  RAS
  //          |            |
  //          |            | identity
  //          V            V
  //         node    ---> RAS
  //                                  -1   -1
  //    priorToNode = [ node_size | 0]  * X   * X * [ prior_size | 0]
  //                  [     0     | 1]              [     0      | 1]
  //
  //                = [         -1             |     ]
  //                  [ node_size * prior_size |  0  ]
  //                  [           0            |  1  ]
  //
  // Note that node_spacing > prior_spacing and thus the following will do
  // .5 becomes 0.
  // but this is OK, since the correspondence is
  //               |  0  |   1  |   prior
  //               |     0      |   node
  // i.e. 1 becomes 0
  *pxn = xp*gca->prior_spacing/gca->node_spacing;
  *pyn = yp*gca->prior_spacing/gca->node_spacing;
  *pzn = zp*gca->prior_spacing/gca->node_spacing;
  // no error should occur
  return errCode;
}

static int
dump_gcan(GCA *gca, GCA_NODE *gcan, FILE *fp, int verbose, GCA_PRIOR *gcap)
{
  int       n, i, j, n1 ;
  GC1D      *gc ;
  float     prior ;
  VECTOR    *v_means = NULL ;
  MATRIX    *m_cov = NULL ;

  if (gcap==NULL)
    return -1;
  // print out gcan labels
  fprintf(fp,
          "node labels at this point -----"
          "---------------------------------\n");
  for (n = 0 ; n < gcan->nlabels ; n++)
  {
    // find the same label in gcap
    for (n1 = 0 ; n1 < gcap->nlabels ; n1++)
      if (gcap->labels[n1] == gcan->labels[n])
        break ;
    // the following won't happen
    if (n1 >= gcap->nlabels)
      continue ;
    prior = getPrior(gcap, gcan->labels[n]) ;
    if (FZERO(prior))
      continue ;
    // get gc for this label
    gc = &gcan->gcs[n] ;
    v_means = load_mean_vector(gc, v_means, gca->ninputs) ;
    m_cov = load_covariance_matrix(gc, m_cov, gca->ninputs) ;
    fprintf(fp, "%d: label %s (%d), prior %2.3f, ntr %d, means " ,
            n, cma_label_to_name(gcan->labels[n]),
            gcan->labels[n], prior, gc->ntraining) ;
    for (i = 0 ; i < gca->ninputs ; i++)
      fprintf(fp, "%2.1f ", VECTOR_ELT(v_means,i+1)) ;
    fprintf(fp, ", cov:\n") ;
    MatrixPrint(fp, m_cov) ;

    if (verbose)
    {
      for (i = 0 ; i < gca->ninputs ; i++)
      {
        for (j = 0 ; j < gca->ninputs ; j++)
        {
          fprintf(fp, "%2.1f\t", *MATRIX_RELT(m_cov,i+1,j+1)) ;
        }
        fprintf(fp, "\n") ;
      }
      // 6 neighbors
      for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
      {
        fprintf(fp, "\tnbr %d (%d,%d,%d): %d labels\n",
                i, xnbr_offset[i], ynbr_offset[i], znbr_offset[i],
                gc->nlabels[i]) ;
        fflush(fp);
        for (j = 0 ; j < gc->nlabels[i] ; j++)
        {
          fprintf(fp, "\t\tlabel %s, prior %2.3f\n",
                  cma_label_to_name(gc->labels[i][j]),
                  gc->label_priors[i][j]) ;
          fflush(fp);
        }
      }
    }
  }
  fprintf(fp, "--------------------------------"
          "--------------------------------\n");
  MatrixFree(&m_cov) ;
  VectorFree(&v_means);
  return(NO_ERROR) ;
}

////////////////////////////////////////////////////////////////
// transform from template -> node
////////////////////////////////////////////////////////////////
int GCAvoxelToNodeReal(GCA *gca, MRI *mri, Real xv, Real yv, Real zv,
                       Real *pxn, Real *pyn, Real *pzn)
{
  //               i_to_r
  //        mri    ----->    RAS
  //         |                |
  //         | we need        | identity
  //         V                V
  //        node   <-----    RAS
  //               r_to_i
  MATRIX *rasFromVoxel = mri->i_to_r__; // extract_i_to_r(mri);
  MATRIX *nodeFromRAS = gca->node_r_to_i__; // extract_r_to_i(gca->mri_node__);
  MATRIX *voxelToNode = gca->tmp__;
  MatrixMultiply(nodeFromRAS, rasFromVoxel, gca->tmp__);

  TransformWithMatrix(voxelToNode, xv, yv, zv, pxn, pyn, pzn);

  // MatrixFree(&rasFromVoxel);
  // MatrixFree(&nodeFromRAS);
  // MatrixFree(&voxelToNode);

  return NO_ERROR;
}

int
GCAvoxelToNode(GCA *gca, MRI *mri, int xv, int yv, int zv, int *pxn,
               int *pyn, int *pzn)
{
  Real xn, yn, zn;
  int   ixn, iyn, izn;
  int errCode = NO_ERROR;

  GCAvoxelToNodeReal(gca, mri, xv, yv, zv, &xn, &yn, &zn);

  // xn, yn, zn are double.  we use voxel center as integer
  ixn = (int) floor(xn);
  iyn = (int) floor(yn);
  izn = (int) floor(zn);
  // if outofbounds, tell it
  errCode = boundsCheck(&ixn, &iyn, &izn, gca->mri_node__);
  //
  *pxn = ixn;
  *pyn = iyn;
  *pzn = izn;

  return errCode;
}

////////////////////////////////////////////////////////////////////
// transform from template -> prior
////////////////////////////////////////////////////////////////////
int GCAvoxelToPriorReal(GCA *gca, MRI *mri, Real xv, Real yv, Real zv,
                        Real *pxp, Real *pyp, Real *pzp)
{
  MATRIX *rasFromVoxel = mri->i_to_r__; //extract_i_to_r(mri);
  MATRIX *priorFromRAS = gca->prior_r_to_i__;
  //extract_r_to_i(gca->mri_prior__);
  MATRIX *voxelToPrior = gca->tmp__;
  MatrixMultiply(priorFromRAS, rasFromVoxel, gca->tmp__);

  TransformWithMatrix(voxelToPrior, xv, yv, zv, pxp, pyp, pzp);

  // MatrixFree(&rasFromVoxel);
  // MatrixFree(&priorFromRAS);
  // MatrixFree(&voxelToPrior);

  return NO_ERROR;
}

int
GCAvoxelToPrior(GCA *gca, MRI *mri, int xv, int yv, int zv,
                int *pxp, int *pyp, int *pzp)
{
  Real xp, yp, zp;
  int ixp, iyp, izp;
  int errCode = NO_ERROR;

  GCAvoxelToPriorReal(gca, mri, xv, yv, zv, &xp, &yp, &zp);

  ixp = (int) floor(xp);
  iyp = (int) floor(yp);
  izp = (int) floor(zp);
  // bound check
  // if outofbounds, tell it
  errCode = boundsCheck(&ixp, &iyp, &izp, gca->mri_prior__);
  //
  *pxp = ixp;
  *pyp = iyp;
  *pzp = izp;

  return errCode;
}

/////////////////////////////////////////////////////////////////////
// transform node->template
/////////////////////////////////////////////////////////////////////
int GCAnodeToVoxelReal(GCA *gca, MRI *mri, Real xn, Real yn, Real zn,
                       Real *pxv, Real *pyv, Real *pzv)
{
  //               r_to_i
  //        mri    <-----    RAS
  //         ^                ^
  //         | we need        | identity
  //         |                |
  //        node   ----->    RAS
  //               i_to_r
  MATRIX *rasFromNode = gca->node_i_to_r__;
  //  extract_i_to_r(gca->mri_node__);
  MATRIX *voxelFromRAS = mri->r_to_i__; //  extract_r_to_i(mri);
  MATRIX *nodeToVoxel = gca->tmp__;
  MatrixMultiply(voxelFromRAS, rasFromNode, gca->tmp__);

  TransformWithMatrix(nodeToVoxel, xn, yn, zn, pxv, pyv, pzv);

  // MatrixFree(&rasFromNode);
  // MatrixFree(&voxelFromRAS);
  // MatrixFree(&nodeToVoxel);

  return NO_ERROR;
}

int
GCAnodeToVoxel(GCA *gca, MRI *mri, int xn, int yn, int zn,
               int *pxv, int *pyv, int *pzv)
{
  Real xv, yv, zv;
  int ixv, iyv, izv;
  int errCode = NO_ERROR;

  GCAnodeToVoxelReal(gca, mri, xn, yn, zn, &xv, &yv, &zv);

  // addition makes overall error smaller
  //             0 picks  1 out of 0, 1, 2, 3 possible choices
  // without it, 0 pickes 0 out of 0, 1, 2, 3 possible choices
  // if you used float, then 0 picks 1.5.
  ixv = (int) floor(xv + gca->node_spacing/2.);
  iyv = (int) floor(yv + gca->node_spacing/2.);
  izv = (int) floor(zv + gca->node_spacing/2.);

  // bound check
  // if outofbounds, tell it
  errCode = boundsCheck(&ixv, &iyv, &izv, mri);
  //

  *pxv = ixv;
  *pyv = iyv;
  *pzv = izv;
  return errCode;
}

//////////////////////////////////////////////////////////////////////
// transform from prior-> template
//////////////////////////////////////////////////////////////////////
int GCApriorToVoxelReal(GCA *gca, MRI *mri, Real xp, Real yp, Real zp,
                        Real *pxv, Real *pyv, Real *pzv)
{
  MATRIX *rasFromPrior = gca->prior_i_to_r__;
  // extract_i_to_r(gca->mri_prior__);
  MATRIX *voxelFromRAS = mri->r_to_i__; // extract_r_to_i(mri);
  MATRIX *priorToVoxel = gca->tmp__;
  MatrixMultiply(voxelFromRAS, rasFromPrior, gca->tmp__);

  // TransformWithMatrix(priorToVoxel, xp, yp, zp, pxv, pyv, pzv);
  TransformWithMatrix(priorToVoxel, xp, yp, zp, pxv, pyv, pzv);

  // MatrixFree(&rasFromPrior);
  // MatrixFree(&voxelFromRAS);
  // MatrixFree(&priorToVoxel);

  return NO_ERROR;
}

int
GCApriorToVoxel(GCA *gca, MRI *mri, int xp, int yp, int zp,
                int *pxv, int *pyv, int *pzv)
{
  Real xv, yv, zv;
  int ixv, iyv, izv;
  int errCode = NO_ERROR;

  GCApriorToVoxelReal(gca, mri, xp, yp, zp, &xv, &yv, &zv);
  // addition makes overall error smaller
  // without it, 0 pickes 0 out of 0, 1 possible choices
  ixv = (int) floor(xv + gca->prior_spacing/2.);
  iyv = (int) floor(yv + gca->prior_spacing/2.);
  izv = (int) floor(zv + gca->prior_spacing/2.);
  // bound check
  // if outofbounds, tell it
  errCode = boundsCheck(&ixv, &iyv, &izv, mri);
  //
  *pxv = ixv;
  *pyv = iyv;
  *pzv = izv;

  errCode = NO_ERROR;

  return errCode;
}

///////////////////////////////////////////////////////////////////////
// transform from source -> template space -> prior
//////////////////////////////////////////////////////////////////////
int
GCAsourceVoxelToPrior(GCA *gca, MRI *mri, TRANSFORM *transform,
                      int xv, int yv, int zv, int *pxp, int *pyp, int *pzp)
{
  float   xt, yt, zt ;
  Real    xrt, yrt, zrt, xrp, yrp, zrp;

  LTA *lta;
  if (transform->type != MORPH_3D_TYPE)
  {
    if (transform->type == LINEAR_VOX_TO_VOX)
    {
      lta = (LTA *) transform->xform;
      // transform point to talairach volume point
      TransformWithMatrix(lta->xforms[0].m_L,
                          xv, yv, zv, &xrt, &yrt, &zrt);
      xt = xrt;
      yt = yrt;
      zt = zrt;
      // TransformSample(transform, xv, yv, zv, &xt, &yt, &zt) ;
    }
    else
      ErrorExit(ERROR_BADPARM, \
                "GCAsourceVoxelToPrior: needs vox-to-vox transform") ;
  }
  else // morph 3d type can go directly from source to template
  {
    TransformSample(transform, xv, yv, zv, &xt, &yt, &zt);
  }
  // get the position in gca from talairach volume
  GCAvoxelToPriorReal(gca, gca->mri_tal__, xt, yt, zt, &xrp, &yrp, &zrp) ;
  *pxp = nint(xrp) ;
  *pyp = nint(yrp) ;
  *pzp = nint(zrp) ;
  if (*pxp < 0 || *pyp < 0 || *pzp < 0 ||
      *pxp >= gca->prior_width ||
      *pyp >= gca->prior_height ||
      *pzp >= gca->prior_depth)
    return(ERROR_BADPARM) ;
  return (NO_ERROR) ;
}

int
GCAsourceFloatVoxelToPrior(GCA *gca, MRI *mri, TRANSFORM *transform,
                           float xv, float yv, float zv, int *pxp, int *pyp, int *pzp)
{
  float   xt, yt, zt ;
  Real    xrt, yrt, zrt, xrp, yrp, zrp;

  LTA *lta;
  if (transform->type != MORPH_3D_TYPE)
  {
    if (transform->type == LINEAR_VOX_TO_VOX)
    {
      lta = (LTA *) transform->xform;
      // transform point to talairach volume point
      TransformWithMatrix(lta->xforms[0].m_L,
                          xv, yv, zv, &xrt, &yrt, &zrt);
      xt = xrt;
      yt = yrt;
      zt = zrt;
      // TransformSample(transform, xv, yv, zv, &xt, &yt, &zt) ;
    }
    else
      ErrorExit(ERROR_BADPARM, \
                "GCAsourceVoxelToPrior: needs vox-to-vox transform") ;
  }
  else // morph 3d type can go directly from source to template
  {
    TransformSample(transform, xv, yv, zv, &xt, &yt, &zt);
  }
  // get the position in gca from talairach volume
  GCAvoxelToPriorReal(gca, gca->mri_tal__, xt, yt, zt, &xrp, &yrp, &zrp) ;
  *pxp = nint(xrp) ;
  *pyp = nint(yrp) ;
  *pzp = nint(zrp) ;
  if (*pxp < 0 || *pyp < 0 || *pzp < 0 ||
      *pxp >= gca->prior_width ||
      *pyp >= gca->prior_height ||
      *pzp >= gca->prior_depth)
    return(ERROR_BADPARM) ;
  return (NO_ERROR) ;
}

/////////////////////////////////////////////////////////////////////
// transform from source -> template space -> prior
/////////////////////////////////////////////////////////////////////
int
GCAsourceVoxelToNode(GCA *gca, MRI *mri, TRANSFORM *transform,
                     int xv, int yv, int zv,
                     int *pxn, int *pyn, int *pzn)
{
  float xt, yt, zt;
  Real  xrt, yrt, zrt ;
  LTA *lta;

  if (transform->type != MORPH_3D_TYPE)
  {
    if (transform->type == LINEAR_VOX_TO_VOX) // from src to talairach volume
    {
      lta = (LTA *) transform->xform;
      // get the talairach position
      TransformWithMatrix(lta->xforms[0].m_L,
                          xv, yv, zv, &xrt, &yrt, &zrt);
      // TransformSample(transform, xv, yv, zv, &xt, &yt, &zt) ;
      xt = xrt;
      yt = yrt;
      zt = zrt;
    }
    else
      ErrorExit(ERROR_BADPARM,
                "GCAsourceVoxelToNode: needs vox-to-vox transform") ;
  }
  else
  {
    TransformSample(transform, xv, yv, zv, &xt, &yt, &zt);
  }
  if (Ggca_x == xv && Ggca_y == yv && Ggca_z == zv && DIAG_VERBOSE_ON)
    fprintf(stdout, "source (%d, %d, %d) to talposition (%.2f, %.2f, %.2f)\n",
            xv, yv, zv, xt, yt, zt);
  fflush(stdout) ;

  // get the position in node from the talairach position
  return GCAvoxelToNode(gca, gca->mri_tal__, xt, yt, zt, pxn, pyn, pzn) ;
}

//////////////////////////////////////////////////////////////////////
// transform from node->template->source
////////////////////////////////////////////////////////////////////
int
GCApriorToSourceVoxelFloat(GCA *gca, MRI *mri, TRANSFORM *transform,
                           int xp, int yp, int zp,
                           float *pxv, float *pyv, float *pzv)
{
  int   width, height, depth;
  Real  xt, yt, zt ;
  float  xv, yv, zv ;
  Real  xc, yc, zc;
  int errCode = NO_ERROR;
  LTA *lta;
  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;
  // go to the template voxel position
  GCApriorToVoxelReal(gca, gca->mri_tal__, xp, yp, zp, &xt, &yt, &zt);
  // got the point in gca->mri_tal__ position
  if (transform->type != MORPH_3D_TYPE)
  {
    if (transform->type == LINEAR_VOX_TO_VOX) // from src to talairach volume
    {
      lta = (LTA *) transform->xform;
      // get the talairach to orig
      TransformWithMatrix(lta->inv_xforms[0].m_L,
                          xt, yt, zt, &xc, &yc, &zc);
      // TransformSampleInverse(transform, xt, yt, zt, &xc, &yc, &zc);
      if (xc < 0)
        errCode = ERROR_BADPARM;
      else if (yc < 0)
        errCode = ERROR_BADPARM;
      else if (zc < 0)
        errCode = ERROR_BADPARM;
      else if (xc > (width-1))
        errCode = ERROR_BADPARM;
      else if (yc > (height-1))
        errCode = ERROR_BADPARM;
      else if (zc > (depth-1))
        errCode = ERROR_BADPARM;
      xv = xc;
      yv = yc;
      zv = zc;
    }
    else
      ErrorExit(ERROR_BADPARM,
                "GCApriorToSourceVoxelFloat: needs vox-to-vox transform") ;
  }
  else // go directly from template to source
  {
    TransformSampleInverse(transform, xt, yt, zt, &xv, &yv, &zv);
  }
  *pxv = xv ;
  *pyv = yv ;
  *pzv = zv ;
  return errCode ;
}

int
GCAnodeToSourceVoxelFloat(GCA *gca, MRI *mri, TRANSFORM *transform,
                          int xn, int yn, int zn,
                          float *pxv, float *pyv, float *pzv)
{
  int   width, height, depth;
  Real  xt, yt, zt ;
  float  xv, yv, zv ;
  Real  xc, yc, zc ;
  int errCode = NO_ERROR;
  LTA *lta;
  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;
  // get template voxel position
  GCAnodeToVoxelReal(gca, gca->mri_tal__, xn, yn, zn, &xt, &yt, &zt) ;
  if (transform->type != MORPH_3D_TYPE)
  {
    lta = (LTA *) transform->xform;
    // get the talairach to orig
    TransformWithMatrix(lta->inv_xforms[0].m_L, xt, yt, zt, &xc, &yc, &zc);
    // TransformSampleInverse(transform, xt, yt, zt, &xc, &yc, &zc);
    if (xc < 0)
      errCode = ERROR_BADPARM;
    else if (yc < 0)
      errCode = ERROR_BADPARM;
    else if (zc < 0)
      errCode = ERROR_BADPARM;
    else if (xc > (width-1))
      errCode = ERROR_BADPARM;
    else if (yc > (height-1))
      errCode = ERROR_BADPARM;
    else if (zc > (depth-1))
      errCode = ERROR_BADPARM;
    xv = xc;
    yv = yc;
    zv = zc;
  }
  else  // template to source
  {
    TransformSampleInverse(transform, xt, yt, zt, &xv, &yv, &zv);
  }
  *pxv = xv ;
  *pyv = yv ;
  *pzv = zv ;
  return errCode;
}

int
GCAnodeToSourceVoxel(GCA *gca, MRI *mri, TRANSFORM *transform,
                     int xn, int yn, int zn,
                     int *pxv, int *pyv, int *pzv)
{
  float  xf, yf, zf ;
  int errCode = NO_ERROR;
  errCode = GCAnodeToSourceVoxelFloat(gca, mri, transform,
                                      xn, yn, zn, &xf, &yf, &zf) ;
  if (xf < 0)
    errCode = ERROR_BADPARM;
  else if (yf <0)
    errCode = ERROR_BADPARM;
  else if (zf < 0)
    errCode = ERROR_BADPARM;
  else if (xf > (mri->width-1))
    errCode = ERROR_BADPARM;
  else if (yf > (mri->height-1))
    errCode = ERROR_BADPARM;
  else if (zf > (mri->depth-1))
    errCode = ERROR_BADPARM;

  *pxv = nint(xf) ;
  *pyv = nint(yf) ;
  *pzv = nint(zf) ;
  return errCode;
}

int
GCApriorToSourceVoxel(GCA *gca, MRI *mri, TRANSFORM *transform,
                      int xp, int yp, int zp,
                      int *pxv, int *pyv, int *pzv)
{
  float  xf, yf, zf ;
  int errCode = NO_ERROR;

  errCode = GCApriorToSourceVoxelFloat(gca, mri, transform,
                                       xp, yp, zp, &xf, &yf, &zf) ;
  if (xf < 0)
    errCode = ERROR_BADPARM;
  else if (yf < 0)
    errCode = ERROR_BADPARM;
  else if (zf < 0)
    errCode = ERROR_BADPARM;
  else if (xf > (mri->width-1))
    errCode = ERROR_BADPARM;
  else if (yf > (mri->height-1))
    errCode = ERROR_BADPARM;
  else if (zf > (mri->depth-1))
    errCode = ERROR_BADPARM;

  *pxv = nint(xf) ;
  *pyv = nint(yf) ;
  *pzv = nint(zf) ;
  return errCode;
}

GCA  *
GCAalloc(int ninputs,
         float prior_spacing, float node_spacing,
         int width, int height, int depth, int flags)
{
  int max_labels ;

  if (flags & GCA_NO_GCS)
    max_labels = 0 ;
  else
    max_labels = DEFAULT_MAX_LABELS_PER_GCAN ;
  return(gcaAllocMax(ninputs, prior_spacing, node_spacing, width,height,depth,
                     max_labels, flags));
}

GCA *
gcaAllocMax(int ninputs,
            float prior_spacing, float node_spacing,
            int width, int height, int depth,
            int max_labels, int flags)
{
  GCA       *gca ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  int       x, y, z ;

  gca = calloc(1, sizeof(GCA)) ;
  if (!gca)
    ErrorExit(ERROR_NOMEMORY, "GCAalloc: could not allocate struct") ;

  gca->ninputs = ninputs ;
  gca->prior_spacing = prior_spacing ;
  gca->node_spacing = node_spacing ;
  gca->type = GCA_UNKNOWN; // mark it as unknown

  // setup default direction cosines
  gca->x_r = -1;
  gca->y_r =  0;
  gca->z_r = 0;
  gca->c_r = 0;
  gca->x_a =  0;
  gca->y_a =  0;
  gca->z_a = 1;
  gca->c_a = 0;
  gca->x_s =  0;
  gca->y_s = -1;
  gca->z_s = 0;
  gca->c_s = 0;
  gca->xsize = 1;
  gca->ysize = 1;
  gca->zsize = 1;
  //
  gca->width = width;
  gca->height = height;
  gca->depth = depth;

  /* ceil gives crazy results, I don't know why */
  gca->node_width = (int)(((float)width/node_spacing)+.99) ;
  gca->node_height = (int)((float)height/node_spacing+.99) ;
  gca->node_depth = (int)(((float)depth/node_spacing)+.99) ;
  gca->prior_width = (int)(((float)width/prior_spacing)+.99) ;
  gca->prior_height = (int)((float)height/prior_spacing+.99) ;
  gca->prior_depth = (int)(((float)depth/prior_spacing)+.99) ;
  gca->flags = flags ;

  gca->nodes = (GCA_NODE ***)calloc(gca->node_width, sizeof(GCA_NODE **)) ;
  if (!gca->nodes)
    ErrorExit(ERROR_NOMEMORY, "GCAalloc: could not allocate nodes") ;

  // setting vlaues gca->nodes volume
  for (x = 0 ; x < gca->node_width ; x++)
  {
    gca->nodes[x] =
      (GCA_NODE **)calloc(gca->node_height, sizeof(GCA_NODE *)) ;
    if (!gca->nodes[x])
      ErrorExit(ERROR_NOMEMORY, "GCAalloc: could not allocate %dth **",x) ;

    for (y = 0 ; y < gca->node_height ; y++)
    {
      gca->nodes[x][y] =
        (GCA_NODE *)calloc(gca->node_depth, sizeof(GCA_NODE)) ;
      if (!gca->nodes[x][y])
        ErrorExit(ERROR_NOMEMORY,
                  "GCAalloc: could not allocate %d,%dth *",x,y);
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        // set max_labels
        gcan->max_labels = max_labels ;

        if (max_labels > 0)
        {
          /* allocate new ones */
          gcan->gcs =
            alloc_gcs(gcan->max_labels, flags, gca->ninputs) ;
          if (!gcan->gcs)
            ErrorExit(ERROR_NOMEMORY,
                      "GCANalloc: couldn't allocate gcs to %d",
                      gcan->max_labels) ;
          // allocate label storage up to max_labels
          gcan->labels =
            (unsigned short *)calloc(gcan->max_labels,
                                    sizeof(unsigned short)) ;
          if (!gcan->labels)
            ErrorExit(ERROR_NOMEMORY,
                      "GCANalloc: couldn't allocate labels to %d",
                      gcan->max_labels) ;
        }
      }
    }
  }

  gca->priors = (GCA_PRIOR ***)calloc(gca->prior_width, sizeof(GCA_PRIOR **)) ;
  if (!gca->priors)
    ErrorExit(ERROR_NOMEMORY, "GCAalloc: could not allocate priors") ;
  // setting values to gca->prior volume
  for (x = 0 ; x < gca->prior_width ; x++)
  {
    gca->priors[x] =
      (GCA_PRIOR **)calloc(gca->prior_height, sizeof(GCA_PRIOR *)) ;
    if (!gca->priors[x])
      ErrorExit(ERROR_NOMEMORY, "GCAalloc: could not allocate %dth **",x) ;

    for (y = 0 ; y < gca->prior_height ; y++)
    {
      gca->priors[x][y] =
        (GCA_PRIOR *)calloc(gca->prior_depth, sizeof(GCA_PRIOR)) ;
      if (!gca->priors[x][y])
        ErrorExit(ERROR_NOMEMORY,
                  "GCAalloc: could not allocate %d,%dth *",x,y);
      for (z = 0 ; z < gca->prior_depth ; z++)
      {
        gcap = &gca->priors[x][y][z] ;
        if (gcap==NULL)
          continue;
        gcap->max_labels = max_labels ;

        if (max_labels > 0)
        {
          /* allocate new ones */
          // allocate label space
          gcap->labels =
            (unsigned short *)calloc(max_labels,
                                    sizeof(unsigned short)) ;
          if (!gcap->labels)
            ErrorExit(ERROR_NOMEMORY, \
                      "GCANalloc: couldn't allocate labels to %d",
                      gcap->max_labels) ;
          // create prior space
          gcap->priors = (float *)calloc(max_labels, sizeof(float)) ;
          if (!gcap->priors)
            ErrorExit(ERROR_NOMEMORY,
                      "GCANalloc: couldn't allocate priors to %d",
                      max_labels) ;
        }
      }
    }
  }
  // setup
  gca->mri_node__ = 0;
  gca->mri_prior__ = 0;
  gca->mri_tal__ = 0;
  // initialize
  gca->node_i_to_r__ = gca->node_r_to_i__ = 0;
  gca->prior_i_to_r__ = gca->prior_r_to_i__ = 0;
  gca->tal_i_to_r__ = gca->tal_r_to_i__ = 0;

  GCAsetup(gca);

  return(gca) ;
}

int
GCAfree(GCA **pgca)
{
  GCA  *gca ;
  int  x, y, z ;

  gca = *pgca ;
  *pgca = NULL ;

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        GCANfree(&gca->nodes[x][y][z], gca->ninputs) ;
      }
      free(gca->nodes[x][y]) ;
    }
    free(gca->nodes[x]) ;
  }
  free(gca->nodes) ;

  for (x = 0 ; x < gca->prior_width ; x++)
  {
    for (y = 0 ; y < gca->prior_height ; y++)
    {
      for (z = 0 ; z < gca->prior_depth ; z++)
      {
        free(gca->priors[x][y][z].labels) ;
        free(gca->priors[x][y][z].priors) ;
      }
      free(gca->priors[x][y]) ;
    }
    free(gca->priors[x]) ;
  }

  free(gca->priors) ;
  GCAcleanup(gca);

  free(gca) ;

  return(NO_ERROR) ;
}

int
GCANfree(GCA_NODE *gcan, int ninputs)
{
  if (gcan->nlabels)
  {
    free(gcan->labels) ;
    free_gcs(gcan->gcs, gcan->nlabels, ninputs) ;
  }
  return(NO_ERROR) ;
}

void PrintInfoOnLabels(GCA *gca, int label, int xn, int yn, int zn,
                       int xp, int yp, int zp,
                       int x, int y, int z)
{
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  GC1D *gc ;
  int  i ;
  // using node to find the label
  gc = GCAfindGC(gca, xn, yn, zn, label) ;
  if (gc)
  {
    gcan = &gca->nodes[xn][yn][zn];
    fprintf(stdout,
            "\n Node (%3d, %3d, %3d) pos (%3d, %3d, %3d) label=%d, labels:",
            xn, yn, zn, x, y, z, label);
    for (i=0; i < gcan->nlabels; ++i)
      fprintf(stdout, "%4d ", gcan->labels[i]);
    fprintf(stdout, "\n");
    gcap = &gca->priors[xp][yp][zp];
    if (gcap==NULL)
      return;
    fprintf(stdout,
            "Prior (%3d, %3d, %3d) pos (%3d, %3d, %3d) label=%d\n",
            xp, yp, zp, x, y, z, label);
    fprintf(stdout, "prior label histogram  (label):");
    for (i=0; i < gcap->nlabels; ++i)
      fprintf(stdout, "%4d ", gcap->labels[i]);
    fprintf(stdout, "\n");
    fprintf(stdout, "                     :(counts):");
    for (i=0; i < gcap->nlabels; ++i)
      fprintf(stdout, "%4.f ", gcap->priors[i]);
    fprintf(stdout, "\n");
    fprintf(stdout, "mean: ");
    for (i = 0 ; i < gca->ninputs ; i++)
      fprintf(stdout, "%2.1f ", gc->means[i] / gc->ntraining) ;
    fprintf(stdout, "\n");
  }
  fflush(stdout) ;
}

int
GCAtrainCovariances(GCA *gca,
                    MRI *mri_inputs,
                    MRI *mri_labels,
                    TRANSFORM *transform)
{
  int    x, y, z, width, height, depth, label, xn, yn, zn ;
  float  vals[MAX_GCA_INPUTS] ;
  int xp, yp, zp;

  /* convert transform to voxel coordinates */

  /* go through each voxel in the input volume and find the canonical
     voxel (and hence the classifier) to which it maps. Then update the
     classifiers statistics based on this voxel's intensity and label.
  */
  width = mri_labels->width ;
  height = mri_labels->height;
  depth = mri_labels->depth ;
  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        // get the segmented value
        label = nint(MRIgetVoxVal(mri_labels, x, y, z,0)) ;

#if 0
        if (!label)
          continue ;
#endif
        // get all input volume values at this point
        load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;

        // src -> talairach -> node
        if (!GCAsourceVoxelToNode(gca, mri_inputs, transform,
                                  x, y, z, &xn, &yn, &zn))
        {
          if (!GCAsourceVoxelToPrior(gca, mri_inputs,
                                     transform, x, y, z,
                                     &xp, &yp, &zp))
          {
            ///////////////// debug code ////////////////////////////
            if ((xp == Gxp && yp == Gyp && zp == Gzp) &&
                (Ggca_label < 0 || Ggca_label == label))
            {
              printf("src (%d, %d, %d), "
                     "prior (%d, %d, %d), "
                     "node (%d, %d, %d), label = %d\n",
                     x,y,z, xp, yp, zp, xn, yn, zn, label);
            }
            ////////////////////////////////////////////////////////
            // update the value
            GCAupdateNodeCovariance(gca, mri_inputs,
                                    xn, yn, zn, vals, label) ;

            //////////////debug code ////////////////////////////
            if (xn == Gxn && yn == Gyn && zn == Gzn)
            {
              fprintf(stdout, "Train Covariance\n");
              PrintInfoOnLabels(gca, label,
                                xn,yn,zn,
                                xp,yp,zp,
                                x,y,z);
            }
            if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z &&
                (label == Ggca_label || Ggca_label < 0))
            {
              GC1D *gc ;
              int  i, nsamples ;
              MATRIX *m ;
              gc = GCAfindGC(gca, xn, yn, zn, label) ;
              if (gc)
              {
                nsamples = gc->ntraining - gc->n_just_priors ;
                /* for no-intensity training */
                if (nsamples < 1)
                  nsamples = 1  ;
                printf("voxel(%d,%d,%d) = ", x, y, z) ;
                for (i = 0 ; i < gca->ninputs ; i++)
                  printf("%d ", nint(vals[i])) ;

                printf(" --> node(%d,%d,%d), "
                       "label %s (%d), mean ",
                       xn, yn, zn,
                       cma_label_to_name(label),label) ;
                for (i = 0 ; i < gca->ninputs ; i++)
                  printf("%2.1f ", gc->means[i]) ;
                printf("\ncovariances (det=%f):\n",
                       covariance_determinant
                       (gc,gca->ninputs)/pow
                       ((double)nsamples,
                        (double)gca->ninputs)) ;
                m =
                  load_covariance_matrix(gc,
                                         NULL,
                                         gca->ninputs) ;
                MatrixScalarMul(m, 1.0/(nsamples), m) ;
                MatrixPrint(stdout, m) ;
                MatrixFree(&m) ;
              }
            }
            /////////////////////////////////////////////////
          }
        } // if (!GCA...)
      }
    }
  }

  fflush(stdout) ;
  return(NO_ERROR) ;
}
#include <unistd.h>

int
GCAtrain(GCA *gca, MRI *mri_inputs, MRI *mri_labels,
         TRANSFORM *transform, GCA *gca_prune,
         int noint)
{
  int    x, y, z, width, height, depth, label, xn, yn, zn,holes_filled,
  /*i, xnbr, ynbr, znbr, xn_nbr, yn_nbr, zn_nbr,*/ xp, yp, zp ;
  float  vals[MAX_GCA_INPUTS] ;
  static int first_time = 1 ;
  FILE *logfp = NULL ;
  static int logging = 0 ;
  GCA_PRIOR *gcap ;
  GCA_NODE  *gcan ;
  MRI       *mri_mapped ;

	gca->total_training++ ;
  mri_mapped = MRIalloc(gca->prior_width, gca->prior_height, gca->prior_depth,
                        MRI_UCHAR) ;
  if (first_time)
  {
    first_time = 0 ;
    logging = getenv("GCA_LOG") != NULL ;
    if (logging)
    {
      printf("logging image intensities to GCA log file 'gca*.log'\n") ;
      for (label = 0 ; label <= MAX_CMA_LABEL ; label++)
      {
        char fname[STRLEN] ;
        sprintf(fname, "gca%d.log", label) ;
        if (FileExists(fname))
          unlink(fname) ;
      }
    }
  }


  /* convert transform to voxel coordinates */

  /* go through each voxel in the input volume and find the canonical
     voxel (and hence the classifier) to which it maps. Then update the
     classifiers statistics based on this voxel's intensity and label.
  */
  // segmented volume
  width = mri_labels->width ;
  height = mri_labels->height;
  depth = mri_labels->depth ;
  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        /// debugging /////////////////////////////////////
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;

        ///////////////////////////////////////////////////

        // get the segmented voxel label
        label = nint(MRIgetVoxVal(mri_labels, x, y, z,0)) ;
#if 0
        if (!label)
          continue ;
#endif
        // get all the values to vals[] in inputs at this point
        // mri_inputs are T1, PD etc.
        load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
        // segmented volume->talairach volume->node
        if (!GCAsourceVoxelToNode(gca, mri_inputs, transform,
                                  x, y, z, &xn, &yn, &zn))
          if (!GCAsourceVoxelToPrior(gca, mri_inputs, transform,
                                     x, y, z, &xp, &yp, &zp))
          {
            // got node point (xn, yn. zn) and
            // prior point (xp, yp, zp) for
            // this label volume point (x, y, z)
            // update the value at this prior point
            if (xp == Gxp && yp == Gyp && zp == Gzp)
            {
              DiagBreak() ;
              if (Ggca_label < 0 || Ggca_label == label)
              {
                printf("src (%d, %d, %d), "
                       "prior (%d, %d, %d), "
                       "node (%d, %d, %d), label = %d\n",
                       x,y,z, xp, yp, zp, xn, yn, zn, label);
              }
            }
            MRIvox(mri_mapped, xp, yp, zp) = 1 ;
            GCAupdatePrior(gca, mri_inputs, xp, yp, zp, label) ;
            if ((GCAupdateNode(gca, mri_inputs, xn, yn, zn,
                               vals,label,gca_prune, noint) ==
                 NO_ERROR) &&
                !(gca->flags & GCA_NO_MRF))
              //                 node        label point
              GCAupdateNodeGibbsPriors(gca, mri_labels,
                                       xn, yn, zn, x, y,z, label);

            /// debugging code //////////////////////////////////////
            if (xn == Gxn && yn == Gyn && zn == Gzn)
            {
              PrintInfoOnLabels(gca, label,
                                xn,yn,zn,
                                xp,yp,zp,
                                x,y,z);
            }
            if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z &&
                (label == Ggca_label || (Ggca_label < 0))
                && (Ggca_nbr_label < 0))
            {
              GC1D *gc ;
              int  i ;
              gc = GCAfindGC(gca, xn, yn, zn, label) ;
              if (gc)
              {
                if (logging)
                {
                  char fname[STRLEN] ;
                  sprintf(fname, "gca%d.log", label) ;
                  logfp = fopen(fname, "a") ;
                }
                printf("voxel(%d,%d,%d) = ", x, y, z) ;
                for (i = 0 ; i < gca->ninputs ; i++)
                {
                  printf("%2.1f ", (vals[i])) ;
                  if (logging)
                    fprintf(logfp, "%2.1f ", (vals[i])) ;
                }

                printf(" --> node(%d,%d,%d), "
                       "label %s (%d), mean ",
                       xn, yn, zn,
                       cma_label_to_name(label),label) ;
                for (i = 0 ; i < gca->ninputs ; i++)
                  printf("%2.1f ", gc->means[i] / gc->ntraining) ;
                printf("\n") ;
                gcan = &gca->nodes[xn][yn][zn];
                printf("   node labels:");
                for (i=0; i < gcan->nlabels; ++i)
                  printf("%d ", gcan->labels[i]);
                printf("\n");
                printf(" --> prior (%d,%d,%d)\n", xp, yp, zp);
                gcap = &gca->priors[xp][yp][zp];
                if (gcap==NULL)
                  continue;
                printf("   prior labels:");
                for (i=0; i < gcap->nlabels; ++i)
                  printf("%d ", gcap->labels[i]);
                printf("\n");
                if (logging)
                {
                  fprintf(logfp, "\n") ;
                  fclose(logfp) ;
                }
              }
              ///////////////////////////////////////////
            }
          }
      }
      if (gca->flags & GCA_NO_MRF)
        continue ;
#if 0
      for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
      {
        xnbr = x+xnbr_offset[i] ;
        ynbr = y+ynbr_offset[i] ;
        znbr = z+znbr_offset[i] ;
        if (!GCAsourceVoxelToNode(gca, mri_inputs, transform,
                                  xnbr, ynbr, znbr,
                                  &xn_nbr,&yn_nbr,&zn_nbr))
        {
          if (xn_nbr == xn && yn_nbr == yn && zn_nbr == zn)
            continue ;  /* only update if it is a different node */

          if (GCAupdateNode(gca, mri_inputs, xn_nbr, yn_nbr, zn_nbr,
                            vals,label,gca_prune,noint) == NO_ERROR)
            GCAupdateNodeGibbsPriors(gca, mri_labels,
                                     xn_nbr, yn_nbr, zn_nbr,
                                     x, y,z, label);
        }
      }
#endif
    }
  }

  for (holes_filled = xp = 0 ; xp < gca->prior_width; xp++)
  {
    for (yp = 0 ; yp < gca->prior_height; yp++)
    {
      for (zp = 0 ; zp < gca->prior_depth; zp++)
      {
        if (MRIvox(mri_mapped, xp, yp, zp) > 0)
          continue ;
        if (!GCApriorToSourceVoxel(gca, mri_inputs, transform,
                                   xp, yp, zp, &x, &y, &z))
        {
          GC1D *gc ;
          label = nint(MRIgetVoxVal(mri_labels, x, y, z, 0)) ;
          GCAupdatePrior(gca, mri_inputs, xp, yp, zp, label) ;
          GCApriorToNode(gca, xp, yp, zp, &xn, &yn, &zn) ;
          gc = GCAfindGC(gca, xn, yn, zn, label) ;
          if (gc == NULL)  // label doesn't exist at this position
          {
            load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
            if ((GCAupdateNode(gca, mri_inputs, xn, yn, zn,
                               vals,label,gca_prune, noint) ==
                 NO_ERROR) && !(gca->flags & GCA_NO_MRF))
              GCAupdateNodeGibbsPriors(gca, mri_labels,
                                       xn, yn, zn, x, y,z, label);

          }
          holes_filled++ ;
        }
      }
    }
  }
  if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
    MRIwrite(mri_mapped, "m.mgz") ;
  if (holes_filled > 0)
    printf("%d prior holes filled\n", holes_filled) ;
  MRIfree(&mri_mapped) ;
  return(NO_ERROR) ;
}

// declare function pointer
static int (*myclose)(FILE *stream);

int
GCAwrite(GCA *gca, char *fname)
{
  FILE      *fp ;
  int       x, y, z, n, i, j ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  GC1D      *gc ;

  if (strstr(fname, ".gcz"))
  {
    char command[STRLEN];
    myclose = pclose;
    strcpy(command, "gzip -f -c > ");
    strcat(command, fname);
    fp = popen(command, "w");
    if (errno)
    {
      pclose(fp);
      errno = 0;
      ErrorReturn(ERROR_BADPARM,
                  (ERROR_BADPARM,
                   "GCAwrite(%s): gzip encountered error",
                   fname)) ;
    }
  }
  else
  {
    myclose = fclose;
    fp  = fopen(fname, "wb") ;
  }
  if (!fp)
    ErrorReturn(ERROR_NOFILE,
                (ERROR_NOFILE,
                 "GCAwrite: could not open GCA %s for writing",fname)) ;

  fwriteFloat(GCA_INT_VERSION, fp) ;
  fwriteFloat(gca->prior_spacing, fp) ;
  fwriteFloat(gca->node_spacing, fp) ;
  fwriteInt(gca->prior_width,fp);
  fwriteInt(gca->prior_height,fp);
  fwriteInt(gca->prior_depth,fp);
  fwriteInt(gca->node_width,fp);
  fwriteInt(gca->node_height,fp);
  fwriteInt(gca->node_depth,fp);
  fwriteInt(gca->ninputs,fp) ;
  fwriteInt(gca->flags, fp) ;

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        if (x == 139 && y == 103 && z == 139)
          /* wm should be pallidum */
          DiagBreak() ;
        gcan = &gca->nodes[x][y][z] ;
        fwriteInt(gcan->nlabels, fp) ;
        fwriteInt(gcan->total_training, fp) ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          int  r, c ;
          gc = &gcan->gcs[n] ;
					fwriteInt(gcan->labels[n], fp) ;
          for (r = 0 ; r < gca->ninputs ; r++)
            fwriteFloat(gc->means[r], fp) ;
          for (r = i = 0 ; r < gca->ninputs ; r++)
            for (c = r ;  c < gca->ninputs ; c++, i++)
              fwriteFloat(gc->covars[i], fp) ;

          if (gca->flags & GCA_NO_MRF)
            continue ;
          for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
          {
            fwriteInt(gc->nlabels[i], fp) ;
            for (j = 0 ; j < gc->nlabels[i] ; j++)
            {
              fwriteInt((int)gc->labels[i][j], fp) ;
              fwriteFloat(gc->label_priors[i][j], fp) ;
            }
          }
        }
      }
    }
  }

  for (x = 0 ; x < gca->prior_width ; x++)
  {
    for (y = 0 ; y < gca->prior_height ; y++)
    {
      for (z = 0 ; z < gca->prior_depth ; z++)
      {
        if (x == 139 && y == 103 && z == 139)
          /* wm should be pallidum */
          DiagBreak() ;
        gcap = &gca->priors[x][y][z] ;
        if (gcap==NULL)
          continue;
        fwriteInt(gcap->nlabels, fp) ;
        fwriteInt(gcap->total_training, fp) ;
        for (n = 0 ; n < gcap->nlabels ; n++)
        {
					fwriteInt((int)gcap->labels[n], fp) ;
          fwriteFloat(gcap->priors[n], fp) ;
        }
      }
    }
  }

  // if (gca->type == GCA_FLASH || gca->type == GCA_PARAM)
  // always write gca->type
  {
    int  n ;

    fwriteInt(FILE_TAG, fp) ;  /* beginning of tagged section */

    /* all tags are format: <int: tag> <int: num> <parm> <parm> .... */
    fwriteInt(TAG_GCA_TYPE, fp) ;
    fwriteInt(1, fp) ;
    fwriteInt(gca->type, fp) ;

    if (gca->type == GCA_FLASH)
    {
      fwriteInt(TAG_PARAMETERS, fp) ;
      fwriteInt(3, fp) ;   /* currently only storing 3 parameters */
      for (n = 0 ; n < gca->ninputs ; n++)
      {
        fwriteFloat(gca->TRs[n], fp) ;
        fwriteFloat(gca->FAs[n], fp) ;
        fwriteFloat(gca->TEs[n], fp) ;
      }
    }
  }

  // write direction cosine information
  fwriteInt(TAG_GCA_DIRCOS, fp);
  fwriteFloat(gca->x_r, fp);
  fwriteFloat(gca->x_a, fp);
  fwriteFloat(gca->x_s, fp);
  fwriteFloat(gca->y_r, fp);
  fwriteFloat(gca->y_a, fp);
  fwriteFloat(gca->y_s, fp);
  fwriteFloat(gca->z_r, fp);
  fwriteFloat(gca->z_a, fp);
  fwriteFloat(gca->z_s, fp);
  fwriteFloat(gca->c_r, fp);
  fwriteFloat(gca->c_a, fp);
  fwriteFloat(gca->c_s, fp);
  fwriteInt(gca->width, fp);
  fwriteInt(gca->height, fp);
  fwriteInt(gca->depth, fp);
  fwriteFloat(gca->xsize, fp);
  fwriteFloat(gca->ysize, fp);
  fwriteFloat(gca->zsize, fp);

  // fclose(fp) ;
  myclose(fp);

  return(NO_ERROR) ;
}

GCA *
GCAread(char *fname)
{
  FILE      *fp ;
  int       x, y, z, n, i, j ;
  GCA       *gca ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  GC1D      *gc ;
  float     version, node_spacing, prior_spacing ;
  int       node_width, node_height, node_depth,
  prior_width, prior_height, prior_depth,
  ninputs, flags ;
  int       tag;

  if (strstr(fname, ".gcz"))
  {
    char command[STRLEN];
    myclose = pclose;
#if defined(Darwin) || defined(SunOS)
    // zcat on Max OS always appends and assumes a .Z extention,
    // whereas we want .gcz
    strcpy(command, "gunzip -c ");
#else
    strcpy(command, "zcat ");
#endif
    strcat(command, fname);
    errno = 0;
    fp = popen(command, "r");
    if (errno)
    {
      pclose(fp);
      errno = 0;
      ErrorReturn(NULL, (ERROR_BADPARM,
                         "GCAread: encountered error executing: '%s'",
                         command)) ;
    }
  }
  else
  {
    myclose = fclose;
    fp  = fopen(fname, "rb") ;
  }
  if (!fp)
    ErrorReturn(NULL,
                (ERROR_NOFILE,
                 "GCAread: could not open GCA %s for reading",fname)) ;

  if (!freadFloatEx(&version, fp))
    ErrorReturn(NULL, (ERROR_BADPARM,"GCAread(%s): could not read file",
                       fname)) ;

  if (version < GCA_UCHAR_VERSION)
  {
    node_spacing = freadFloat(fp) ;
    node_width = freadInt(fp);
    node_height = freadInt(fp);
    node_depth = freadInt(fp);
    ninputs = freadInt(fp) ;
    if (version == 3.0)
      flags = freadInt(fp) ;
    else
      flags = 0 ;

    gca = gcaAllocMax(ninputs, node_spacing, \
                      node_spacing, node_spacing*node_width,
                      node_spacing*node_height, \
                      node_spacing*node_depth, 0, flags) ;
    if (!gca)
      ErrorReturn(NULL, (Gerror, NULL)) ;

    for (x = 0 ; x < gca->node_width ; x++)
    {
      for (y = 0 ; y < gca->node_height ; y++)
      {
        for (z = 0 ; z < gca->node_depth ; z++)
        {
          if (x == 28 && y == 39 && z == 39)
            DiagBreak() ;
          gcan = &gca->nodes[x][y][z] ;
          gcan->nlabels = freadInt(fp) ;
          gcan->total_training = freadInt(fp) ;
          if (gcan->nlabels)
          {
            gcan->labels = \
                           (unsigned short *)calloc(gcan->nlabels,
                                                   sizeof(unsigned short)) ;
            if (!gcan->labels)
              ErrorExit(ERROR_NOMEMORY,
                        "GCAread(%s): could not allocate %d "
                        "labels @ (%d,%d,%d)",
                        fname, gcan->nlabels, x, y, z) ;
            gcan->gcs = alloc_gcs(gcan->nlabels,
                                  flags, gca->ninputs) ;
            if (!gcan->gcs)
              ErrorExit(ERROR_NOMEMORY,
                        "GCAread(%s); could not allocated %d gcs "
                        "@ (%d,%d,%d)",
                        fname, gcan->nlabels, x, y, z) ;
          }
          else // no labels assigned to this node
          {
            gcan->labels = 0;
            gcan->gcs = 0;
          }
          for (n = 0 ; n < gcan->nlabels ; n++)
          {
            int r, c;
            gc = &gcan->gcs[n] ;
            gcan->labels[n] = (unsigned short)fgetc(fp) ;
            for (r = 0 ; r < gca->ninputs ; r++)
              gc->means[r] = freadFloat(fp) ;
            for (i = r = 0 ; r < gca->ninputs ; r++)
              for (c = r ; c < gca->ninputs ; c++, i++)
                gc->covars[i] = freadFloat(fp) ;
            if (gca->flags & GCA_NO_MRF)
              continue ;
            for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
            {
              gc->nlabels[i] = freadInt(fp) ;

#if 1
              /* allocate new ones */
              gc->label_priors[i] =
                (float *)calloc(gc->nlabels[i],sizeof(float));
              if (!gc->label_priors[i])
                ErrorExit(ERROR_NOMEMORY, "GCAread(%s): "
                          "couldn't expand gcs to %d",
                          fname,gc->nlabels) ;
              gc->labels[i] =
                (unsigned short *)calloc(gc->nlabels[i], \
                                        sizeof(unsigned short)) ;
              if (!gc->labels)
                ErrorExit(ERROR_NOMEMORY,
                          "GCAread(%s): couldn't expand "
                          "labels to %d",
                          fname, gc->nlabels[i]) ;
#endif
              for (j = 0 ; j < gc->nlabels[i] ; j++)
              {
                gc->labels[i][j] = (unsigned short)freadInt(fp) ;
                gc->label_priors[i][j] = freadFloat(fp) ;
              }
            }
          }
        }
      }
    }
  }
  else   /* current version - stores priors at different
                            resolution than densities */
  {
    if (!FEQUAL(version, GCA_UCHAR_VERSION) &&
				!FEQUAL(version, GCA_INT_VERSION))
    {
      // fclose(fp) ;
      myclose(fp);
      ErrorReturn(NULL, (ERROR_BADFILE,
                         "GCAread(%s), version #%2.1f found, "
                         "%2.1f expected",
                         fname, version, GCA_INT_VERSION)) ;
    }
    prior_spacing = freadFloat(fp) ;
    node_spacing = freadFloat(fp) ;
    prior_width = freadInt(fp);
    prior_height = freadInt(fp);
    prior_depth = freadInt(fp);
    node_width = freadInt(fp);
    node_height = freadInt(fp);
    node_depth = freadInt(fp);
    ninputs = freadInt(fp) ;
    flags = freadInt(fp) ;

    gca = gcaAllocMax(ninputs, prior_spacing, node_spacing,
                      node_spacing*node_width,
                      node_spacing*node_height,
                      node_spacing*node_depth, 0, flags) ;
    if (!gca)
      ErrorReturn(NULL, (Gdiag, NULL)) ;

    for (x = 0 ; x < gca->node_width ; x++)
    {
      for (y = 0 ; y < gca->node_height ; y++)
      {
        for (z = 0 ; z < gca->node_depth ; z++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;
          gcan = &gca->nodes[x][y][z] ;
          gcan->nlabels = freadInt(fp) ;
          gcan->total_training = freadInt(fp) ;
          if (gcan->nlabels)
          {
            gcan->labels =
              (unsigned short *)calloc(gcan->nlabels,
                                      sizeof(unsigned short)) ;
            if (!gcan->labels)
              ErrorExit(ERROR_NOMEMORY, "GCAread(%s): could not "
                        "allocate %d "
                        "labels @ (%d,%d,%d)",
                        fname, gcan->nlabels, x, y, z) ;
            gcan->gcs = alloc_gcs(gcan->nlabels,
                                  flags,
                                  gca->ninputs) ;
            if (!gcan->gcs)
              ErrorExit(ERROR_NOMEMORY,
                        "GCAread(%s); could not allocated %d gcs "
                        "@ (%d,%d,%d)",
                        fname, gcan->nlabels, x, y, z) ;
          }
          else // no labels at this node
          {
            gcan->labels = 0;
            gcan->gcs = 0;
          }
          for (n = 0 ; n < gcan->nlabels ; n++)
          {
            int  r, c ;

            gc = &gcan->gcs[n] ;

						if (version == GCA_UCHAR_VERSION)
							gcan->labels[n] = (unsigned short)fgetc(fp) ;
						else
							gcan->labels[n] = (unsigned short)freadInt(fp) ;

            for (r = 0 ; r < gca->ninputs ; r++)
              gc->means[r] = freadFloat(fp) ;
            for (i = r = 0 ; r < gca->ninputs ; r++)
              for (c = r ; c < gca->ninputs ; c++, i++)
                gc->covars[i] = freadFloat(fp) ;
            if (gca->flags & GCA_NO_MRF)
              continue ;
            for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
            {
              gc->nlabels[i] = freadInt(fp) ;

              /* allocate new ones */
              gc->label_priors[i] =
                (float *)calloc(gc->nlabels[i],sizeof(float));
              if (!gc->label_priors[i])
                ErrorExit(ERROR_NOMEMORY, "GCAread(%s): "
                          "couldn't expand gcs to %d",
                          fname,gc->nlabels) ;
              gc->labels[i] =
                (unsigned short *)calloc(gc->nlabels[i], \
                                        sizeof(unsigned short)) ;
              if (!gc->labels)
                ErrorExit(ERROR_NOMEMORY,
                          "GCAread(%s): couldn't expand "
                          "labels to %d",
                          fname, gc->nlabels[i]) ;
              for (j = 0 ; j < gc->nlabels[i] ; j++)
              {
                gc->labels[i][j] = (unsigned short)freadInt(fp) ;
                gc->label_priors[i][j] = freadFloat(fp) ;
              }
            }
          }
        }
      }
    }

    for (x = 0 ; x < gca->prior_width ; x++)
    {
      for (y = 0 ; y < gca->prior_height ; y++)
      {
        for (z = 0 ; z < gca->prior_depth ; z++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;
          gcap = &gca->priors[x][y][z] ;
          if (gcap==NULL)
            continue;
          gcap->nlabels = freadInt(fp) ;
          gcap->total_training = freadInt(fp) ;
          if (gcap->nlabels)
          {
            gcap->labels =
              (unsigned short *)calloc(gcap->nlabels, \
                                      sizeof(unsigned short)) ;
            if (!gcap->labels)
              ErrorExit(ERROR_NOMEMORY, "GCAread(%s): could not "
                        "allocate %d "
                        "labels @ (%d,%d,%d)",
                        fname, gcap->nlabels, x, y, z) ;
            gcap->priors = (float *)calloc(gcap->nlabels,
                                           sizeof(float)) ;
            if (!gcap->priors)
              ErrorExit(ERROR_NOMEMORY, "GCAread(%s): could "
                        "not allocate %d "
                        "priors @ (%d,%d,%d)",
                        fname, gcap->nlabels, x, y, z) ;
          }
          else // no labels assigned to this priors
          {
            gcap->labels = 0;
            gcap->priors = 0;
          }
          for (n = 0 ; n < gcap->nlabels ; n++)
          {
						if (version == GCA_UCHAR_VERSION)
							gcap->labels[n] = (unsigned short)fgetc(fp) ;
						else
							gcap->labels[n] = (unsigned short)freadInt(fp) ;
            gcap->priors[n] = freadFloat(fp) ;
          }
        }
      }
    }
  }

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        int xp, yp, zp ;

        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[x][y][z] ;
        if (gcaNodeToPrior(gca, x, y, z, &xp, &yp, &zp)==NO_ERROR)
        {
          gcap = &gca->priors[xp][yp][zp] ;
          if (gcap==NULL)
            continue;
          for (n = 0 ; n < gcan->nlabels ; n++)
          {
            gc = &gcan->gcs[n] ;
            gc->ntraining =
              gcan->total_training * getPrior(gcap,gcan->labels[n]) ;
          }
        }
      }
    }
  }

  // if (!feof(fp))  // this does not work ;-)
  // feof(fp) check does not work, since feof is not signaled until you read
  while (freadIntEx(&tag, fp))
  {
    int  n, nparms ;

    // tag = freadInt(fp) ;
    if (tag == FILE_TAG) /* beginning of tagged section */
    {
      // while (!feof(fp))
      while (freadIntEx(&tag, fp))
      {
        /* all tags are format:
           <int: tag> <int: num> <parm> <parm> .... */
        // tag = freadInt(fp) ;
        switch (tag)
        {
        case TAG_GCA_TYPE:
          freadInt(fp) ;   /* skip num=1 */
          gca->type = freadInt(fp) ;
          if (DIAG_VERBOSE_ON) switch (gca->type)
            {
            case GCA_NORMAL:
              printf("setting gca type = Normal gca type\n");
              break;
            case GCA_PARAM:
              printf("setting gca type = T1/PD gca type\n");
              break;
            case GCA_FLASH:
              printf("setting gca type = FLASH gca type\n");
              break;
            default:
              printf("setting gca type = Unknown\n");
              gca->type=GCA_UNKNOWN;
              break;
            }
          break ;
        case TAG_PARAMETERS:
          nparms = freadInt(fp) ;
          /* how many MR parameters are stored */
          printf("reading %d MR parameters out of GCA header...\n",
                 nparms) ;
          for (n = 0 ; n < gca->ninputs ; n++)
          {
            gca->TRs[n] = freadFloat(fp) ;
            gca->FAs[n] = freadFloat(fp) ;
            gca->TEs[n] = freadFloat(fp) ;
            printf("input %d: TR=%2.1f msec, FA=%2.1f deg, "
                   "TE=%2.1f msec\n",
                   n,
                   gca->TRs[n],
                   DEGREES(gca->FAs[n]), gca->TEs[n]) ;
          }
          break ;
        case TAG_GCA_DIRCOS:
          gca->x_r = freadFloat(fp);
          gca->x_a = freadFloat(fp);
          gca->x_s = freadFloat(fp);
          gca->y_r = freadFloat(fp);
          gca->y_a = freadFloat(fp);
          gca->y_s = freadFloat(fp);
          gca->z_r = freadFloat(fp);
          gca->z_a = freadFloat(fp);
          gca->z_s = freadFloat(fp);
          gca->c_r = freadFloat(fp);
          gca->c_a = freadFloat(fp);
          gca->c_s = freadFloat(fp);
          gca->width = freadInt(fp);
          gca->height = freadInt(fp);
          gca->depth = freadInt(fp);
          gca->xsize = freadFloat(fp);
          gca->ysize = freadFloat(fp);
          gca->zsize = freadFloat(fp);

          if (Gdiag & DIAG_SHOW && DIAG_VERBOSE_ON)
          {
            printf("Direction cosines read:\n");
            printf(" x_r = % .4f, y_r = % .4f, z_r = % .4f\n",
                   gca->x_r, gca->y_r, gca->z_r);
            printf(" x_a = % .4f, y_a = % .4f, z_a = % .4f\n",
                   gca->x_a, gca->y_a, gca->z_a);
            printf(" x_s = % .4f, y_s = % .4f, z_s = % .4f\n",
                   gca->x_s, gca->y_s, gca->z_s);
            printf(" c_r = % .4f, c_a = % .4f, c_s = % .4f\n",
                   gca->c_r, gca->c_a, gca->c_s);
          }
          break;
        default:
          ErrorPrintf(ERROR_BADFILE,
                      "GCAread(%s): unknown tag %x\n", fname, tag) ;
          break ;
        }
      }
    }
  }

  GCAsetup(gca);

  // fclose(fp) ;
  myclose(fp);

  return(gca) ;
}

static int
GCAupdatePrior(GCA *gca, MRI *mri, int xn, int yn, int zn, int label)
{
  int       n ;
  GCA_PRIOR *gcap ;

  if (label >= MAX_CMA_LABEL)
    ErrorReturn(ERROR_BADPARM,
                (ERROR_BADPARM,
                 "GCAupdatePrior(%d, %d, %d, %d): label out of range",
                 xn, yn, zn, label)) ;

  if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z)
    DiagBreak() ;

  gcap = &gca->priors[xn][yn][zn] ;
  if (gcap==NULL)
    return -1;
  // find the label histogram index n
  for (n = 0 ; n < gcap->nlabels ; n++)
  {
    if (gcap->labels[n] == label)
      break ;
  }
  // if index is beyond what we have, then
  if (n >= gcap->nlabels)  /* have to allocate a new classifier */
  {

    if (n >= gcap->max_labels)
    {
      int  old_max_labels ;
      unsigned short *old_labels ;
      float *old_priors ;

      old_max_labels = gcap->max_labels ;
      gcap->max_labels += 2 ;
      old_labels = gcap->labels ;
      old_priors = gcap->priors ;

      /* allocate new ones */
      gcap->priors = (float *)calloc(gcap->max_labels, sizeof(float)) ;

      if (!gcap->priors)
        ErrorExit(ERROR_NOMEMORY,
                  "GCANupdatePriors: couldn't expand priors to %d",
                  gcap->max_labels) ;
      gcap->labels =
        (unsigned short *)calloc(gcap->max_labels, sizeof(unsigned short)) ;
      if (!gcap->labels)
        ErrorExit(ERROR_NOMEMORY,
                  "GCANupdatePriors: couldn't expand labels to %d",
                  gcap->max_labels) ;

      /* copy the old ones over */
      memmove(gcap->priors, old_priors, old_max_labels*sizeof(float)) ;
      memmove(gcap->labels, old_labels,
              old_max_labels*sizeof(unsigned short)) ;

      /* free the old ones */
      free(old_priors) ;
      free(old_labels) ;
    }
    // add one
    gcap->nlabels++ ;
  }

  /* these will be updated when training is complete */
  // give the value at n
  gcap->priors[n] += 1.0f ; // increment counter
  gcap->total_training++ ;  // increment training counter
  gcap->labels[n] = label ; // save the label value

  return(NO_ERROR) ;
}

static int
GCAupdateNodeCovariance(GCA *gca, MRI *mri,
                        int xn, int yn, int zn, float *vals,int label)
{
  int      n, r, c, v ;
  GCA_NODE *gcan ;
  GC1D     *gc ;


  if (label >= MAX_CMA_LABEL)
    ErrorReturn(ERROR_BADPARM,
                (ERROR_BADPARM,
                 "GCAupdateNodeCovariance(%d, %d, %d, %d): label out of range",
                 xn, yn, zn, label)) ;

  ///////////// debug ////////////////////////////////
  if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z && label == Ggca_label)
    DiagBreak() ;
  if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z)
    DiagBreak() ;
  ////////////////////////////////////////////////////

  gcan = &gca->nodes[xn][yn][zn] ;

  for (n = 0 ; n < gcan->nlabels ; n++)
  {
    if (gcan->labels[n] == label)
      break ;
  }
  if (n >= gcan->nlabels)
    ErrorExit(ERROR_BADPARM,
              "GCAupdateNodeCovariance(%d, %d, %d, %d): could not find label",
              xn, yn, zn, label) ;

  gc = &gcan->gcs[n] ;

  /* remove means from vals */
  for (r = 0 ; r < gca->ninputs ; r++)
    vals[r] -= gc->means[r] ;

  /* these will be normalized when training is complete */
  for (v = r = 0 ; r < gca->ninputs ; r++)
  {
    for (c = r ; c < gca->ninputs ; c++, v++)
      gc->covars[v] += vals[r]*vals[c] ;
  }

  /* put means back in vals so caller isn't confused */
  for (r = 0 ; r < gca->ninputs ; r++)
    vals[r] += gc->means[r] ;

  return(NO_ERROR) ;
}

static int
GCAupdateNode(GCA *gca, MRI *mri,
              int xn, int yn, int zn, float *vals, int label,
              GCA *gca_prune, int noint)
{
  int      n, i ;
  GCA_NODE *gcan ;
  GC1D     *gc ;


  if (label >= MAX_CMA_LABEL)
    ErrorReturn(ERROR_BADPARM,
                (ERROR_BADPARM,
                 "GCAupdateNode(%d, %d, %d, %d): label out of range",
                 xn, yn, zn, label)) ;

  if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z && label == Ggca_label)
    DiagBreak() ;
  if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z)
    DiagBreak() ;

  // if non-zero label and gca_prune is there
  if (label > 0 && gca_prune != NULL && !noint)
  {
    GCA_NODE *gcan_prune ;
    GC1D     *gc_prune ;
    int      nprune ;

    gcan_prune = &gca_prune->nodes[xn][yn][zn] ;

    for (nprune = 0 ; nprune < gcan_prune->nlabels ; nprune++)
    {
      if (gcan_prune->labels[nprune] == label)
        break ;
    }
    if (nprune >= gcan_prune->nlabels)
      ErrorPrintf(ERROR_BADPARM,
                  "WARNING: pruning GCA at (%d,%d,%d) doesn't "
                  "contain label %d", xn, yn, zn, label) ;
    gc_prune = &gcan_prune->gcs[nprune] ;

    if (sqrt(GCAmahDist(gc_prune, vals, gca->ninputs)) > 2)
      /* more than 2 stds from mean */
    {
#if 0
      if (xn == 23 && yn == 30 && zn == 32)
      {
        printf(
          "pruning val %2.0f, label %d @ "
          "(%d,%d,%d),u=%2.1f, std=%2.1f\n"
          ,val, label, xn,yn,zn,mean, std) ;
        DiagBreak() ;
      }
#endif
      total_pruned++ ;
      return(ERROR_BAD_PARM) ;
    }
  }

  // get one at this point
  gcan = &gca->nodes[xn][yn][zn] ;

  // look for this label in the array
  for (n = 0 ; n < gcan->nlabels ; n++)
  {
    if (gcan->labels[n] == label)
      break ;
  }
  // if not found
  if (n >= gcan->nlabels)  /* have to allocate a new classifier */
  {

    if (n >= gcan->max_labels)
    {
      int  old_max_labels ;
      unsigned short *old_labels ;
      GC1D *old_gcs ;

      old_max_labels = gcan->max_labels ;
      gcan->max_labels += 2 ;
      old_labels = gcan->labels ;
      old_gcs = gcan->gcs ;

      /* allocate new ones */
#if 0
      gcan->gcs = (GC1D *)calloc(gcan->max_labels, sizeof(GC1D)) ;
#else
      gcan->gcs = alloc_gcs(gcan->max_labels, gca->flags, gca->ninputs) ;
#endif

      if (!gcan->gcs)
        ErrorExit(ERROR_NOMEMORY,
                  "GCANupdateNode: couldn't expand gcs to %d",
                  gcan->max_labels) ;
      gcan->labels =
        (unsigned short *)calloc(gcan->max_labels, sizeof(unsigned short)) ;
      if (!gcan->labels)
        ErrorExit(ERROR_NOMEMORY,
                  "GCANupdateNode: couldn't expand labels to %d",
                  gcan->max_labels) ;

      /* copy the old ones over */
#if 0
      memmove(gcan->gcs, old_gcs, old_max_labels*sizeof(GC1D)) ;
#else
      copy_gcs(old_max_labels, old_gcs, gcan->gcs, gca->ninputs) ;
#endif
      memmove(gcan->labels, old_labels,
              old_max_labels*sizeof(unsigned short)) ;

      /* free the old ones */
      free(old_gcs) ;
      free(old_labels) ;
    }
    gcan->nlabels++ ;
  }

  gc = &gcan->gcs[n] ;

  /* these will be updated when training is complete */
  if (noint)
    gc->n_just_priors++ ;
  else
  {
    // vals[] array is the values of inputs at this point
    for (i = 0 ; i < gca->ninputs ; i++)
      gc->means[i] += vals[i] ;
    // get the mean valu (note it is not divided by ninputs!)
    /*gc->var += val*val ; */
  }
  if (gc->n_just_priors >= gc->ntraining)
    DiagBreak() ;
  gcan->total_training++ ;

  gcan->labels[n] = label ;
  gc->ntraining++ ;

  return(NO_ERROR) ;
}
int
GCAcompleteMeanTraining(GCA *gca)
{
  int       x, y, z, n, total_nodes, \
  total_gcs, i, j, holes_filled, total_brain_gcs,\
  total_brain_nodes, r ;
  float     nsamples ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  GC1D      *gc ;

  total_nodes = gca->node_width*gca->node_height*gca->node_depth ;
  total_brain_nodes = total_gcs = total_brain_gcs = 0 ;
  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        total_gcs += gcan->nlabels ;
        if (gcan->nlabels > 1 || !IS_UNKNOWN(gcan->labels[0]))
        {
          total_brain_gcs += gcan->nlabels ;
          total_brain_nodes++ ;
        }

        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          gc = &gcan->gcs[n] ;
          nsamples = gc->ntraining ;
          if ((gca->flags & GCA_NO_MRF) == 0)
          {
            for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
            {
              for (j = 0 ; j < gc->nlabels[i] ; j++)
              {
                gc->label_priors[i][j] /= (float)nsamples ;
                check_finite(
                  "GCAcompleteMeanTraining: "
                  "label_priors",
                  gc->label_priors[i][j]) ;
              }
            }
          }

          nsamples -= gc->n_just_priors ;
          /* for no-intensity training */
          if (nsamples > 0)
          {
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              gc->means[r] /= nsamples ;
              check_finite("GCAcompleteMeanTraining: mean",
                           gc->means[r]) ;
            }
          }
          else
          {
            int r ;
            for (r = 0 ; r < gca->ninputs ; r++)
              gc->means[r] = -1 ;  /* mark it for later processing */
          }
        }
      }
    }
  }

  for (x = 0 ; x < gca->prior_width ; x++)
  {
    for (y = 0 ; y < gca->prior_height ; y++)
    {
      for (z = 0 ; z < gca->prior_depth ; z++)
      {
        gcap = &gca->priors[x][y][z] ;
        if (gcap==NULL)
          continue;
        for (n = 0 ; n < gcap->nlabels ; n++)
        {
          gcap->priors[n] /= (float)gcap->total_training ;
          check_finite("GCAcompleteMeanTraining: priors",
                       gcap->priors[n]) ;
        }

      }
    }
  }
  printf("filling holes in the GCA...\n") ;
  for (holes_filled = x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          gc = &gcan->gcs[n] ;
          nsamples = gc->ntraining - gc->n_just_priors ;
          if (nsamples <= 0)
          {
            GC1D *gc_nbr ;
            int  r, i ;

            gc_nbr = findClosestValidGC(gca, x, y, z,
                                        gcan->labels[n], 0) ;
            if (!gc_nbr)
            {
              ErrorPrintf(ERROR_BADPARM,
                          "gca(%d,%d,%d,%d) - could not "
                          "find valid nbr label "
                          "%s (%d)", x,  y, z, n,
                          cma_label_to_name(gcan->labels[n]),
                          gcan->labels[n]) ;
              continue ;
            }
            holes_filled++ ;
            for (i = r = 0 ; r < gca->ninputs ; r++)
            {
              gc->means[r] = gc_nbr->means[r] ;
            }
            if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
              printf("filling hole @ (%d, %d, %d)\n", x, y, z) ;
          }
        }
      }
    }
  }

  printf("%d classifiers: %2.1f per node, %2.2f in brain (%d holes filled)\n",
         total_gcs, (float)total_gcs/(float)total_nodes,
         (float)total_brain_gcs/(float)total_brain_nodes,holes_filled) ;
  if (total_pruned > 0)
  {
    printf("%d samples pruned during training\n", total_pruned) ;
    total_pruned = 0 ;
  }
  return(NO_ERROR) ;
}

int
GCAcompleteCovarianceTraining(GCA *gca)
{
  int       x, y, z, n, holes_filled, r, c,v, nregs = 0, nsamples ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;

        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label == gcan->labels[n] || Ggca_label < 0))
            DiagBreak() ;
          gc = &gcan->gcs[n] ;
          nsamples = gc->ntraining - gc->n_just_priors ;
          /* for no-intensity training */
          if (nsamples > 0)
          {
            for (r = v = 0 ; r < gca->ninputs ; r++)
            {
              check_finite("GCAcompleteCovarianceTraining: mean",
                           gc->means[r]) ;
              if (nsamples > 1)
              {
                for (c = r ; c < gca->ninputs ; c++, v++)
                {
                  gc->covars[v] /= (float)(nsamples-1) ;
                  check_finite("GCAcompleteCovarianceTraining:"
                               " covar",
                               gc->covars[v]) ;
                  if (r == c)
                    /* diagonal should be positive definite */
                  {
                    if (gc->covars[v] < -0.1)
                      DiagBreak() ;
                  }
                }
              }
            }
          }
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (gcan->labels[n] == Ggca_label || Ggca_label < 0))
          {
            MATRIX *m ;
            double det ;

            det = covariance_determinant(gc, gca->ninputs) ;
            printf("final covariance matrix for %s "
                   "(nsamples=%d, det=%f):\n",
                   cma_label_to_name(gcan->labels[n]),
                   nsamples, det) ;
            m = load_covariance_matrix(gc, NULL, gca->ninputs) ;
            MatrixPrint(stdout, m) ;
            MatrixFree(&m) ;
            fflush(stdout) ;
          }
        }
      }
    }
  }

  holes_filled = 0 ;
#if 0
  printf("filling holes in the GCA...\n") ;
  for (holes_filled = x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label == gcan->labels[n] || Ggca_label < 0))
            DiagBreak() ;
          gc = &gcan->gcs[n] ;
          nsamples = gc->ntraining - gc->n_just_priors ;
          /* for no-intensity training */
#define MIN_GC_SAMPLES(gca) ((gca->ninputs*(gca->ninputs+1))/2)
          if (gc->means[0] < 0 || nsamples < MIN_GC_SAMPLES(gca))
          {
            GC1D *gc_nbr ;
            int  r, c, i ;

            gc_nbr = findClosestValidGC(gca, x, y, z,
                                        gcan->labels[n], 1) ;
            if (!gc_nbr)
            {
              ErrorPrintf(ERROR_BADPARM,
                          "gca(%d,%d,%d,%d) - could not "
                          "find valid nbr label "
                          "%s (%d)", x,  y, z, n,
                          cma_label_to_name(gcan->labels[n]),
                          gcan->labels[n]) ;
              continue ;
            }
            holes_filled++ ;
            for (i = r = 0 ; r < gca->ninputs ; r++)
            {
              gc->means[r] = gc_nbr->means[r] ;
              for (c = r ; c < gca->ninputs ; c++, i++)
                gc->covars[i] = gc_nbr->covars[i] ;
            }
            if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
                (Ggca_label == gcan->labels[n] || Ggca_label < 0))
              printf("filling hole @ (%d, %d, %d)\n", x, y, z) ;
          }
        }
      }
    }
  }
#endif

  GCAfixSingularCovarianceMatrices(gca) ;
  /* shouldn't need the code that follows, but haven't made sure yet */

  /* find and fix singular covariance matrices */
  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          MATRIX *m_cov, *m_cov_inv ;
          double det ;

          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label == gcan->labels[n] || Ggca_label < 0))
            DiagBreak() ;
          gc = &gcan->gcs[n] ;
          m_cov = load_covariance_matrix(gc, NULL, gca->ninputs) ;
          m_cov_inv = MatrixInverse(m_cov, NULL) ;
          det = covariance_determinant(gc, gca->ninputs) ;
          if (det <= 0 )
          {
            MatrixFree(&m_cov_inv) ;
            m_cov_inv = NULL ;
          }
          if (m_cov_inv == NULL)  /* singular */
          {
            MATRIX *m_I ;
            m_I = MatrixIdentity(gca->ninputs, NULL) ;
            MatrixScalarMul(m_I, MIN_VAR, m_I) ;
            MatrixAdd(m_cov, m_I, m_cov) ;
            m_cov_inv = MatrixInverse(m_cov, NULL) ;
            if (m_cov_inv == NULL)
              ErrorExit(ERROR_BADPARM,
                        "GCAcompleteCovarianceTraining: cannot "
                        "regularize singular covariance matrix at "
                        "(%d,%d,%d):%d",
                        x, y, z, n) ;
            det = covariance_determinant(gc, gca->ninputs) ;
            if (det < 0)
              DiagBreak() ;
            for (v = r = 0 ; r < gca->ninputs ; r++)
              for (c = r ; c < gca->ninputs ; c++, v++)
                gc->covars[v] = *MATRIX_RELT(m_cov, r+1, c+1) ;
            MatrixFree(&m_I) ;
            nregs++ ;
          }

          MatrixFree(&m_cov_inv) ;
          MatrixFree(&m_cov) ;
        }
      }
    }
  }
  gcaCheck(gca) ;

  if (total_pruned > 0)
  {
    printf("%d samples pruned during training\n", total_pruned) ;
    total_pruned = 0 ;
  }
  return(NO_ERROR) ;
}

MRI  *
GCAlabel(MRI *mri_inputs, GCA *gca, MRI *mri_dst, TRANSFORM *transform)
{
  int       x, y, z, width, height, depth, label, xn, yn, zn, n, num_pv,
  use_partial_volume_stuff ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  GC1D      *gc ;
  float    /*dist,*/ max_p, p, vals[MAX_GCA_INPUTS] ;
#if INTERP_PRIOR
  float     prior ;
#endif

  use_partial_volume_stuff = (getenv("USE_PARTIAL_VOLUME_STUFF") != NULL);
  if (use_partial_volume_stuff)
    printf("using partial volume calculations in labeling...\n") ;

  // labeled volume has the same property of the inputs
  if (!mri_dst)
  {
    mri_dst = MRIalloc(mri_inputs->width,
                       mri_inputs->height,
                       mri_inputs->depth,
                       MRI_UCHAR) ;
    if (!mri_dst)
      ErrorExit(ERROR_NOMEMORY, "GCAlabel: could not allocate dst") ;
    MRIcopyHeader(mri_inputs, mri_dst) ;
  }


  /* go through each voxel in the input volume and find the canonical
     voxel (and hence the classifier) to which it maps. Then update the
     classifiers statistics based on this voxel's intensity and label.
  */
  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;

        if (x == width/2 && y == height/2 && z == depth/2)
          DiagBreak() ;

        if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                  transform, x, y, z, &xn, &yn, &zn))
        {
          load_vals(mri_inputs, x, y, z, vals, gca->ninputs);

#if 0
          if (x == 153 && y == 119 && z == 117)
            /* wm should be hippo (1484) */
          {
            Gx = xn ;
            Gy = yn ;
            Gz = zn ;
            DiagBreak() ;
          }
#endif

          gcan = &gca->nodes[xn][yn][zn] ;
          gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
          if (gcap==NULL)
            continue;
          label = 0 ;
          max_p = 2*GIBBS_NEIGHBORS*BIG_AND_NEGATIVE ;
          // going through gcap labels
          for (n = 0 ; n < gcap->nlabels ; n++)
          {
#if 0
            p = GCAcomputePosteriorDensity(gcap, gcan, n, vals,
                                           gca->ninputs) ;
#else
            gc = GCAfindGC(gca, xn, yn, zn, gcap->labels[n]) ;
            if (gc == NULL)
            {
              MRIsetVoxVal(mri_dst, x, y, z,0,0); // unknown
              continue ;
            }
#if INTERP_PRIOR
            prior = gcaComputePrior(gca,
                                    mri_inputs,
                                    transform,
                                    x, y, z,
                                    gcap->labels[n]) ;
            p = gcaComputeLogDensity(gc, vals,
                                     gca->ninputs,
                                     prior,
                                     gcap->labels[n]) ;
#else
            p = gcaComputeLogDensity(gc, vals,
                                     gca->ninputs,
                                     gcap->priors[n],
                                     gcap->labels[n]) ;
#endif
#endif
            // look for largest p
            if (p > max_p)
            {
              max_p = p ;
              label = gcap->labels[n] ;
            }
          }
          if (use_partial_volume_stuff)
            //////////// start of partial volume stuff
          {
            int n1, l1, l2, max_l1, max_l2, max_n1, max_n2 ;
            double max_p_pv ;

            max_p_pv = -10000  ;
            if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
              DiagBreak() ;
            max_l1 = label ;
            max_l2 = max_n1 = max_n2 = 0 ;
            for (n = 0 ; n < gcap->nlabels ; n++)
              for (n1 = n+1 ; n1 < gcap->nlabels ; n1++)
              {
                l1 = gcap->labels[n] ;
                l2 = gcap->labels[n1] ;
                p = compute_partial_volume_log_posterior
                    (gca, gcan, gcap, vals, l1, l2) ;
                if (p > max_p_pv)
                {
                  max_l1 = l1 ;
                  max_l2 = l2 ;
                  max_p_pv = p ;
                  max_n1 = n ;
                  max_n2 = n1 ;
                }
                if (p > max_p && l1 != label && l2 != label)
                  DiagBreak() ;
              }

            /* not the label picked before - change it */
            if (max_p_pv > max_p &&
                max_l1 != label &&
                max_l2 != label)
            {
              double p1, p2 ;

              gc = GCAfindGC(gca, xn, yn, zn, max_l1) ;
              p1 =
                gcaComputeLogDensity(gc, vals, gca->ninputs,
                                     gcap->priors[max_n1],max_l1) ;
              gc = GCAfindGC(gca, xn, yn, zn, max_l2) ;
              p2 =
                gcaComputeLogDensity(gc, vals, gca->ninputs,
                                     gcap->priors[max_n2],max_l2) ;
              num_pv++ ;
              if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
                printf("label @ %d, %d, %d: partial volume "
                       "from %s to %s\n",
                       x, y, z, cma_label_to_name(label),
                       cma_label_to_name
                       (p1 > p2 ? max_l1 : max_l2)) ;
              label = p1 > p2 ? max_l1 : max_l2 ;
              DiagBreak() ;
            }
          }
          //////////// end of partial volume stuff

          // found the label
          ///////////////////////// debug code /////////////////////
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          {
            int i ;
            printf("(%d, %d, %d): inputs=", x, y, z) ;
            for (i = 0 ; i < gca->ninputs ; i++)
              printf("%2.1f ", vals[i]) ;

            printf("\nprior label %s (%d), log(p)=%2.2e, "
                   "node (%d, %d, %d)\n",
                   cma_label_to_name(label), label, max_p,
                   xn, yn, zn) ;
            dump_gcan(gca, gcan, stdout, 1, gcap) ;
          }
          /////////////////////////////////////////////
          // set the value
          MRIsetVoxVal(mri_dst, x, y, z, 0, label) ;
        }
        else
          MRIsetVoxVal(mri_dst, x, y, z, 0, 0) ; // unknown
      } // z loop
    } // y loop
  } // x loop

  return(mri_dst) ;
}

MRI  *
GCAlabelProbabilities(MRI *mri_inputs,
                      GCA *gca,
                      MRI *mri_dst,
                      TRANSFORM *transform)
{
  int       x, y, z, width, height, depth, label,
  xn, yn, zn, n ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  GC1D      *gc ;
  double    max_p, p, total_p ;
  float     vals[MAX_GCA_INPUTS] ;

  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  if (!mri_dst)
  {
    mri_dst = MRIalloc(width, height, depth, MRI_UCHAR) ;
    if (!mri_dst)
      ErrorExit(ERROR_NOMEMORY, "GCAlabel: could not allocate dst") ;
    MRIcopyHeader(mri_inputs, mri_dst) ;
  }

  /* go through each voxel in the input volume and find the canonical
     voxel (and hence the classifier) to which it maps. Then update the
     classifiers statistics based on this voxel's intensity and label.
  */
  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        /// debug code /////////////////////////
        if (x == 152 && y == 126 && z == 127)
          DiagBreak() ;
        if (x == 63 && y == 107 && z == 120)
          DiagBreak() ;
        ///////////////////////////////////////

        load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
        if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                  transform, x, y, z, &xn, &yn, &zn))
        {
          gcan = &gca->nodes[xn][yn][zn] ;
          gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
          if (gcap == NULL || gcap->nlabels <= 0)
            continue;
          label = 0 ;
          max_p = 2*GIBBS_NEIGHBORS*BIG_AND_NEGATIVE ;
          // go through labels and find the one with max probability
          for (total_p = 0.0, n = 0 ; n < gcan->nlabels ; n++)
          {
            gc = &gcan->gcs[n] ;

            /* compute 1-d Mahalanobis distance */
            p = GCAcomputePosteriorDensity(gcap, gcan, n, vals,
                                           gca->ninputs) ;
            if (p > max_p)
            {
              max_p = p ;
              label = gcan->labels[n] ;
            }
            total_p += p ;
          }
          max_p = 255.0* max_p / total_p ;
          if (max_p > 255) max_p = 255 ;
          MRIsetVoxVal(mri_dst, x, y, z, 0, (BUFTYPE)max_p) ;
        }
        else
          MRIsetVoxVal(mri_dst, x, y, z, 0,255); // 0;
      }
    }
  }

  return(mri_dst) ;
}

MRI  *
GCAcomputeProbabilities(MRI *mri_inputs, GCA *gca, MRI *mri_labels,
                        MRI *mri_dst, TRANSFORM *transform)
{
  int       x, y, z, width, height, depth, label,
  xn, yn, zn, n ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  GC1D      *gc ;
  double    label_p, p, total_p ;
  float     vals[MAX_GCA_INPUTS] ;

  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  if (!mri_dst)
  {
    mri_dst = MRIalloc(width, height, depth, MRI_UCHAR) ;
    if (!mri_dst)
      ErrorExit(ERROR_NOMEMORY, "GCAlabel: could not allocate dst") ;
    MRIcopyHeader(mri_inputs, mri_dst) ;
  }


  /* go through each voxel in the input volume and find the canonical
     voxel (and hence the classifier) to which it maps. Then update the
     classifiers statistics based on this voxel's intensity and label.
  */
  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
#if 0
        if (x == 152 && y == 126 && z == 127)
          DiagBreak() ;
        if (x == 63 && y == 107 && z == 120)
          DiagBreak() ;
#endif
        load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
        if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                  transform, x, y, z, &xn, &yn, &zn))
        {
          label = nint(MRIgetVoxVal(mri_labels, x, y, z, 0)) ;

          gcan = &gca->nodes[xn][yn][zn] ;
          gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
          if (gcap==NULL)
            continue;
          for (label_p = total_p = 0.0, n = 0 ;
               n < gcan->nlabels ;
               n++)
          {
            gc = &gcan->gcs[n] ;

            p = GCAcomputePosteriorDensity(gcap, gcan, n, vals,
                                           gca->ninputs) ;
            if (label == gcan->labels[n])
            {
              label_p = p ;
            }
            total_p += p ;
          }
          label_p = 255.0* label_p / total_p ;
          if (label_p > 255) label_p = 255 ;
          MRIsetVoxVal(mri_dst, x, y, z,0,(BUFTYPE)label_p) ;
        }
      }
    }
  }

  return(mri_dst) ;
}

#define STARTING_T   500

MRI  *
GCAannealUnlikelyVoxels(MRI *mri_inputs,
                        GCA *gca,
                        MRI *mri_dst,
                        TRANSFORM *transform,
                        int max_iter,
                        MRI  *mri_fixed)
{
  int       x, y, z, width, depth, height, *x_indices, *y_indices, *z_indices,
  nindices, index, iter, nchanged, xn, yn, zn, n, nbad,
  old_label  ;
  double    log_likelihood, T, delta_E, p, rn, new_ll, old_ll,
  total_likelihood ;
  GCA_NODE  *gcan ;
  MRI       *mri_bad ;

  width = mri_inputs->width ;
  height = mri_inputs->height ;
  depth = mri_inputs->depth ;
  mri_bad = MRIalloc(width, height, depth, MRI_UCHAR) ;
  MRIcopyHeader(mri_inputs, mri_bad);

  for (nindices = x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        if (mri_fixed && MRIvox(mri_fixed, x, y, z) > 0)
          continue ;

        if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                  transform, x, y, z, &xn, &yn, &zn))
        {
          log_likelihood =
            gcaVoxelGibbsLogLikelihood(gca, mri_dst,
                                       mri_inputs, x, y, z,transform,
                                       PRIOR_FACTOR);
          gcan = &gca->nodes[xn][yn][zn] ;
          if (log_likelihood < log(1.0f/(float)gcan->total_training))
          {
            MRIvox(mri_bad, x, y, z) = 1 ;
            nindices++ ;
          }
        }
      }
    }
  }

  printf("annealing %d voxels...\n", nindices) ;
  x_indices = (int *)calloc(nindices, sizeof(int)) ;
  y_indices = (int *)calloc(nindices, sizeof(int)) ;
  z_indices = (int *)calloc(nindices, sizeof(int)) ;
  for (index = x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        if (MRIvox(mri_bad, x, y, z) > 0)
        {
          x_indices[index] = x ;
          y_indices[index] = y ;
          z_indices[index] = z ;
          index++ ;
        }
      }
    }
  }

  MRIfree(&mri_bad) ;
  T = STARTING_T ;
  iter = 0 ;
  do
  {
    total_likelihood = 0.0 ;
    for (nbad = nchanged = index = 0 ; index < nindices ; index++)
    {
      x = x_indices[index] ;
      y = y_indices[index] ;
      z = z_indices[index] ;
      if (x == 155 && y == 126 && z == 128)
        DiagBreak() ;

      /* find the node associated with this coordinate and classify */
      if (!GCAsourceVoxelToNode(gca, mri_inputs, transform, x, y, z,
                                &xn, &yn, &zn))
      {
        gcan = &gca->nodes[xn][yn][zn] ;

        if (gcan->nlabels == 1)
          continue ;
        n = (int)randomNumber(0.0, (double)gcan->nlabels-0.0001) ;
        if (gcan->labels[n] == nint(MRIgetVoxVal(mri_dst, x, y, z,0)))
          continue ;
        old_ll =
          gcaNbhdGibbsLogLikelihood(gca, mri_dst, mri_inputs,
                                    x, y, z, transform,
                                    PRIOR_FACTOR) ;
        old_label = nint(MRIgetVoxVal(mri_dst, x, y, z,0)) ;
        MRIsetVoxVal(mri_dst, x, y, z, 0, gcan->labels[n]) ;
        new_ll =
          gcaNbhdGibbsLogLikelihood(gca, mri_dst, mri_inputs,
                                    x, y, z, transform,
                                    PRIOR_FACTOR) ;
        delta_E = new_ll - old_ll ;
        p = exp(delta_E / T) ;
        rn = randomNumber(0.0, 1.0) ;

        if (p > rn)
        {
          if (new_ll < log(1.0f/(float)gcan->total_training))
            nbad++ ;
          nchanged++ ;
          total_likelihood += new_ll ;
        }
        else
        {
          total_likelihood += old_ll ;
          if (old_ll < log(1.0f/(float)gcan->total_training))
            nbad++ ;
          MRIsetVoxVal(mri_dst, x, y, z,0,old_label) ;
        }
      }
    }
    T = T * 0.99 ;
    fprintf(stdout, "%03d: T = %2.2f, nchanged %d, nbad = %d, ll=%2.2f\n",
            iter, T, nchanged, nbad, total_likelihood/(double)nindices) ;
    if (!nchanged)
      break ;
  }
  while (iter++ < max_iter) ;

  free(x_indices) ;
  free(y_indices) ;
  free(z_indices) ;
  fflush(stdout) ;
  return(mri_dst) ;
}

GCA  *
GCAreduce(GCA *gca_src)
{
#if 0
  GCA       *gca_dst ;
  int       xs, ys, zs, xd, yd, zd, swidth, sheight, sdepth,
  ns, nd, dwidth, dheight, ddepth, dof ;
  float     spacing = gca_src->spacing ;
  GCA_NODE  *gcan_src, *gcan_dst ;
  GC1D      *gc_src, *gc_dst ;

  swidth = gca_src->width ;
  sheight = gca_src->height;
  sdepth = gca_src->depth;

  gca_dst =
    GCAalloc(gca_src->ninputs, spacing*2, spacing*swidth,
             spacing*gca_src->height,spacing*sdepth, gca_src->flags) ;


  dwidth = gca_dst->width ;
  dheight = gca_dst->height;
  ddepth = gca_dst->depth;

  for (zs = 0 ; zs < sdepth ; zs++)
  {
    for (ys = 0 ; ys < sheight ; ys++)
    {
      for (xs = 0 ; xs < swidth ; xs++)
      {
        xd = xs/2 ;
        yd = ys/2 ;
        zd = zs/2 ;
        if (xd == 15 && yd == 22 && zd == 35)
          DiagBreak() ;
        gcan_src = &gca_src->nodes[xs][ys][zs] ;
        gcan_dst = &gca_dst->nodes[xd][yd][zd] ;

        for (ns = 0 ; ns < gcan_src->nlabels ; ns++)
        {
          gc_src = &gcan_src->gcs[ns] ;
          dof = gc_src->prior * gcan_src->total_training ;

          /* find label in destination */
          for (nd = 0 ; nd < gcan_dst->nlabels ; nd++)
          {
            if (gcan_dst->labels[nd] == gcan_src->labels[ns])
              break ;
          }
          if (nd >= gcan_dst->nlabels)  /* couldn't find it */
          {
            if (nd >= gcan_dst->max_labels)
            {
              int  old_max_labels ;
              char *old_labels ;
              GC1D *old_gcs ;

              old_max_labels = gcan_dst->max_labels ;
              gcan_dst->max_labels += 2 ;
              old_labels = gcan_dst->labels ;
              old_gcs = gcan_dst->gcs ;

              /* allocate new ones */
#if 0
              gcan_dst->gcs =
                (GC1D *)calloc(gcan_dst->max_labels,
                               sizeof(GC1D)) ;
#else
              gcan_dst->gcs =
                alloc_gcs(gcan_dst->max_labels,
                          gca_dst->flags, gca_dst->ninputs) ;
#endif
              if (!gcan_dst->gcs)
                ErrorExit(ERROR_NOMEMORY,
                          "GCANreduce: couldn't expand gcs to %d",
                          gcan_dst->max_labels) ;
              gcan_dst->labels =
                (unsigned short *)calloc(gcan_dst->max_labels,
                                        sizeof(unsigned short)) ;
              if (!gcan_dst->labels)
                ErrorExit(ERROR_NOMEMORY,
                          "GCANupdateNode: couldn't expand "
                          "labels to %d",
                          gcan_dst->max_labels) ;

              /* copy the old ones over */
#if 0
              memmove(gcan_dst->gcs, old_gcs,
                      old_max_labels*sizeof(GC1D));
#else
              copy_gcs(old_max_labels, old_gcs,
                       gcan_dst->gcs, gca->ninputs) ;
#endif

              memmove(gcan_dst->labels, old_labels,
                      old_max_labels*sizeof(unsigned short)) ;

              /* free the old ones */
              free(old_gcs) ;
              free(old_labels) ;
            }
            gcan_dst->nlabels++ ;
          }
          gc_dst = &gcan_dst->gcs[nd] ;
          gc_dst->mean += dof*gc_src->mean ;
          gc_dst->var += dof*(gc_src->var) ;
          gc_dst->prior += dof ;
          gcan_dst->total_training += dof ;
          gcan_dst->labels[nd] = gcan_src->labels[ns] ;
        }
      }
    }
  }

  for (xd = 0 ; xd < gca_dst->width ; xd++)
  {
    for (yd = 0 ; yd < gca_dst->height ; yd++)
    {
      for (zd = 0 ; zd < gca_dst->depth ; zd++)
      {
        gcan_dst = &gca_dst->nodes[xd][yd][zd] ;
        for (nd = 0 ; nd < gcan_dst->nlabels ; nd++)
        {
          float var ;

          gc_dst = &gcan_dst->gcs[nd] ;
          dof = gc_dst->prior ;
          /* prior is count of # of occurences now */
          gc_dst->mean /= dof ;
          var = gc_dst->var / dof ;
          if (var < -0.1)
            DiagBreak() ;
          if (var < MIN_VAR)
            var = MIN_VAR ;
          gc_dst->var = var ;
          gc_dst->prior /= (float)gcan_dst->total_training ;
        }
      }
    }
  }
  return(gca_dst) ;
#else
  /* have to update to include priors at different resolution than nodes */
  ErrorReturn(NULL, (ERROR_UNSUPPORTED,
                     "GCAreduce: not currently supported") ;) ;
#endif
}


typedef struct
{
  int   label ;
  float prob ;
  int   index ;
}
LABEL_PROB ;

static int compare_sort_probabilities(const void *plp1, const void *plp2);
MRI *
GCAclassify(MRI *mri_inputs,GCA *gca,MRI *mri_dst,
            TRANSFORM *transform,int max_labels)
{
  int        x, y, z, width, height, depth,
  xn, yn, zn, n ;
  GCA_NODE   *gcan ;
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  float      max_p, p, total_p ;
  LABEL_PROB label_probs[1000] ;
  float      vals[MAX_GCA_INPUTS] ;

  if (max_labels > MAX_LABELS_PER_GCAN || max_labels <= 0)
    max_labels = MAX_LABELS_PER_GCAN ;

  if (!mri_dst)
  {
    int width = mri_inputs->width, height = mri_inputs->height,
                                            depth = mri_inputs->depth ;

    mri_dst = MRIallocSequence(width, height, depth,
                               MRI_UCHAR, 2*max_labels) ;
    if (!mri_dst)
      ErrorExit(ERROR_NOMEMORY, "GCAlabel: could not allocate dst") ;
    MRIcopyHeader(mri_inputs, mri_dst) ;
  }

  /* go through each voxel in the input volume and find the canonical
     voxel (and hence the classifier) to which it maps. Then update the
     classifiers statistics based on this voxel's intensity and label.
  */
  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        if (x == 67 && y == 87 && z == 114)
          DiagBreak() ;

        load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;

        /* find the node associated with this coordinate and classify */
        if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                  transform, x, y, z, &xn, &yn, &zn))
        {
          gcan = &gca->nodes[xn][yn][zn] ;
          gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
          if (gcap==NULL)
            continue;
          max_p = 2*GIBBS_NEIGHBORS*BIG_AND_NEGATIVE ;
          for (total_p = 0.0, n = 0 ; n < gcan->nlabels ; n++)
          {
            gc = &gcan->gcs[n] ;

            p = GCAcomputePosteriorDensity(gcap, gcan, n, vals,
                                           gca->ninputs) ;
            total_p += p ;
            label_probs[n].prob = p ;
            label_probs[n].label = gcan->labels[n] ;
          }
          /* now sort the labels and probabilities */
          qsort(label_probs, gcan->nlabels, sizeof(LABEL_PROB),
                compare_sort_probabilities) ;

          for (n = 0 ; n < max_labels ; n++)
          {
            if (n < gcan->nlabels)
            {
              MRIseq_vox(mri_dst, x, y, z, n*2) =
                label_probs[n].label ;
              MRIseq_vox(mri_dst, x, y, z, n*2+1) =
                (BUFTYPE)nint(255.0*label_probs[n].prob/total_p) ;
            }
            else
            {
              MRIseq_vox(mri_dst, x, y, z, n*2) = 255 ;
              MRIseq_vox(mri_dst, x, y, z, n*2+1) = 0 ;
            }
          }
        }
      }
    }
  }

  return(mri_dst) ;
}


static int
compare_sort_probabilities(const void *plp1, const void *plp2)
{
  LABEL_PROB  *lp1, *lp2 ;

  lp1 = (LABEL_PROB *)plp1 ;
  lp2 = (LABEL_PROB *)plp2 ;

  if (lp1->prob > lp2->prob)
    return(1) ;
  else if (lp1->prob < lp2->prob)
    return(-1) ;

  return(0) ;
}

int
GCAremoveOutlyingSamples(GCA *gca, GCA_SAMPLE *gcas, MRI *mri_inputs,
                         TRANSFORM *transform,int nsamples, float nsigma)
{
  int        x, y, z, width, height, depth,
  i, nremoved, xp, yp, zp ;
  double     dist ;
  float      vals[MAX_GCA_INPUTS] ;
  /*  GC1D       *gc ;*/


  /* go through each GC in the sample and compute the probability of
     the image at that point.
  */
  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  TransformInvert(transform, mri_inputs) ;
  for (nremoved = i = 0 ; i < nsamples ; i++)
  {
    if (i == Gdiag_no)
      DiagBreak() ;
    if (Gdiag_no == gcas[i].label)
      DiagBreak() ;
    if (gcas[i].label <= 0)
      continue ;

    xp = gcas[i].xp ;
    yp = gcas[i].yp ;
    zp = gcas[i].zp ;
    if (!GCApriorToSourceVoxel(gca, mri_inputs, transform,
                               xp, yp, zp, &x, &y, &z))
    {
      load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;

      if (xp == Gxp && yp == Gyp && zp == Gzp)
        DiagBreak() ;

#if 0
      gc = GCAfindPriorGC(gca, xp, yp, zp, gcas[i].label) ;
      if (gc == NULL)
      {
        ErrorPrintf(ERROR_BADPARM,
                    "gc %d not found in GCAremoveOutlyingSamples!", i) ;
        continue ;
      }
#endif

      dist = sqrt(GCAsampleMahDist(&gcas[i], vals, gca->ninputs)) ;
      if (dist >= nsigma)
      {
        nremoved++ ;
        gcas[i].log_p = BIG_AND_NEGATIVE ;
        gcas[i].label = 0 ;
      }
    }
  }

  if (DIAG_VERBOSE_ON)
    printf("%d outlying samples removed...\n", nremoved) ;

  return(NO_ERROR) ;
}
float
GCAnormalizedLogSampleProbability(GCA *gca, GCA_SAMPLE *gcas,
                                  MRI *mri_inputs,
                                  TRANSFORM *transform, int nsamples)
{
  int        x, y, z, width, height, depth,
  xn, yn, zn, i, n, xp, yp, zp ;
  GCA_NODE   *gcan ;
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  double     total_log_p, log_p, norm_log_p ;
  float      vals[MAX_GCA_INPUTS] ;


  /* go through each GC in the sample and compute the probability of
     the image at that point.
  */
  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  TransformInvert(transform, mri_inputs) ;
  for (total_log_p = 0.0, i = 0 ; i < nsamples ; i++)
  {
    if (i == Gdiag_no)
      DiagBreak() ;
    if (Gdiag_no == gcas[i].label)
      DiagBreak() ;

    xp = gcas[i].xp ;
    yp = gcas[i].yp ;
    zp = gcas[i].zp ;
    // do the processing for valid points only
    if (!GCApriorToSourceVoxel(gca, mri_inputs,
                               transform, xp, yp, zp, &x, &y, &z))
      if (!GCApriorToNode(gca, xp, yp, zp, &xn, &yn, &zn))
      {
        load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
        // use node and prior values
        gcan = &gca->nodes[xn][yn][zn] ;
        gcap = &gca->priors[xp][yp][zp] ;
        if (gcap==NULL)
          continue;
        if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z)
          DiagBreak() ;

        for (norm_log_p = 0.0f, n = 0 ; n < gcan->nlabels ; n++)
        {
          gc = &gcan->gcs[n] ;
          norm_log_p +=
            GCAcomputeConditionalDensity(gc, vals, gca->ninputs,
                                         gcan->labels[n]) ;
        }
        norm_log_p = log(norm_log_p) ;
        gc = GCAfindPriorGC(gca, xp, yp, zp, gcas[i].label) ;
        log_p = GCAcomputeConditionalDensity(gc, vals, gca->ninputs,
                                             gcas[i].label) ;
        log_p = log(log_p) + log(gcas[i].prior) ;
        log_p -= norm_log_p ;
        total_log_p += log_p ;
        gcas[i].log_p = log_p ;

        if (!check_finite("1", total_log_p))
        {
          fprintf(stdout,
                  "total log p not finite at (%d, %d, %d)\n", x, y, z) ;
          DiagBreak();
        }
      }
  }
  fflush(stdout) ;
  return((float)total_log_p) ;
}

float
GCAcomputeLogSampleProbability(GCA *gca,
                               GCA_SAMPLE *gcas,
                               MRI *mri_inputs,
                               TRANSFORM *transform,
                               int nsamples)
{
  int        x, y, z, width, height, depth, i, xp, yp, zp ;
  float      vals[MAX_GCA_INPUTS] ;
  double     total_log_p, log_p ;
  int        countOutside = 0;
  double     outside_log_p = 0.;
  /* go through each GC in the sample and compute the probability of
     the image at that point.
  */
  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  // store inverse transformation .. forward:input->gca template,
  // inv: gca template->input
  TransformInvert(transform, mri_inputs) ;

  // go through all sample points
  for (total_log_p = 0.0, i = 0 ; i < nsamples ; i++)
  {
    /////////////////// diag code /////////////////////////////
    if (i == Gdiag_no)
      DiagBreak() ;
    if (Gdiag_no == gcas[i].label)
      DiagBreak() ;
    if (i == Gdiag_no ||
        (gcas[i].xp == Gxp && gcas[i].yp == Gyp && gcas[i].zp == Gzp))
      DiagBreak() ;
    ///////////////////////////////////////////////////////////

    // get prior coordinates
    xp = gcas[i].xp ;
    yp = gcas[i].yp ;
    zp = gcas[i].zp ;
    // if it is inside the source voxel
    if (!GCApriorToSourceVoxel(gca, mri_inputs, transform,
                               xp, yp, zp, &x, &y, &z))
    {
      // (x,y,z) is the source voxel position
      gcas[i].x = x ;
      gcas[i].y = y ;
      gcas[i].z = z ;
      // get values from all inputs
      load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
      log_p = gcaComputeSampleLogDensity(&gcas[i], vals, gca->ninputs) ;
      total_log_p += log_p ;
      gcas[i].log_p = log_p ;

      if (!check_finite("2", total_log_p))
      {
        fprintf(stdout,
                "total log p not finite at (%d, %d, %d)\n",
                x, y, z) ;
        DiagBreak() ;
      }
    }
    else  // outside the voxel
    {
      log_p = -1000000; // BIG_AND_NEGATIVE;
      // log(VERY_UNLIKELY); // BIG_AND_NEGATIVE;
      total_log_p += log_p;
      gcas[i].log_p = log_p;
      outside_log_p += log_p;
      countOutside++;
    }
  }

#ifndef __OPTIMIZE__
#if 0
  if (nsamples > 3000)
    fprintf(stdout, "good samples %d (outside %d) log_p = %.1f "
            "(outside %.1f)\n",
            nsamples-countOutside, countOutside, total_log_p, outside_log_p);
#endif
#endif
  fflush(stdout) ;

  return((float)total_log_p) ;
}

float
GCAcomputeLogSampleProbabilityUsingCoords(GCA *gca, GCA_SAMPLE *gcas,
    MRI *mri_inputs,
    TRANSFORM *transform, int nsamples)
{
  int        x, y, z, width, height, depth, xp, yp, zp,
  xn, yn, zn, i ;
  float      vals[MAX_GCA_INPUTS] ;
  double     total_log_p, log_p ;


  /* go through each GC in the sample and compute the probability of
     the image at that point.
  */
  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  for (total_log_p = 0.0, i = 0 ; i < nsamples ; i++)
  {
    if (i == Gdiag_no)
      DiagBreak() ;
    if (Gdiag_no == gcas[i].label)
      DiagBreak() ;

    xp = gcas[i].xp ;
    yp = gcas[i].yp ;
    zp = gcas[i].zp ;
    // do the processing only for valid points
    if (!GCApriorToNode(gca, xp, yp, zp, &xn, &yn, &zn))
    {
      x = gcas[i].x ;
      y = gcas[i].y ;
      z = gcas[i].z ;
      load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;

      log_p = gcaComputeSampleLogDensity(&gcas[i], vals, gca->ninputs) ;
      total_log_p += log_p ;
      gcas[i].log_p = log_p ;

      if (!check_finite("3", total_log_p))
      {
        DiagBreak() ;
        fprintf(stdout,
                "total log p not finite at (%d, %d, %d)\n", x, y, z) ;
      }
    }
    else
    {
      log_p = log(VERY_UNLIKELY); // BIG_AND_NEGATIVE;
      total_log_p += log_p;
      gcas[i].log_p = log_p;
    }
  }
  fflush(stdout) ;

  return((float)total_log_p) ;
}
/*
  compute the probability of the image given the transform and the class
  stats.
*/
float
GCAcomputeLogImageProbability(GCA *gca, MRI *mri_inputs, MRI *mri_labels,
                              TRANSFORM *transform)
{
  int        x, y, z, width, height, depth,
  xn, yn, zn, n, label ;
  GCA_NODE   *gcan ;
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  double     total_log_p ;
  float      vals[MAX_GCA_INPUTS] ;


  /* go through each voxel in the input volume and find the canonical
     voxel (and hence the classifier) to which it maps. Then update the
     classifiers statistics based on this voxel's intensity and label.
  */
  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  for (total_log_p = 0.0, x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        if (x == 85 && y == 89 && z == 135)
          DiagBreak() ;

        load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
        label = nint(MRIgetVoxVal(mri_labels, x, y, z, 0)) ;

        /* find the node associated with this coordinate and classify */
        if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                  transform, x, y, z, &xn, &yn, &zn))
        {
          gcan = &gca->nodes[xn][yn][zn] ;
          gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
          if (gcap==NULL)
            continue;
          for (n = 0 ; n < gcan->nlabels ; n++)
          {
            if (gcan->labels[n] == label)
              break ;
          }
          if (n < gcan->nlabels)
          {
            gc = &gcan->gcs[n] ;

            total_log_p +=
              gcaComputeLogDensity(gc, vals,
                                   gca->ninputs,
                                   getPrior(gcap, label),
                                   label) ;
#if 0
            -log(sqrt(gc->var)) -
            0.5 * (dist*dist/gc->var) +
            log(getPrior(gcap, label)) ;
#endif
            if (!check_finite("4", total_log_p))
            {
              DiagBreak() ;
              fprintf(stdout,
                      "total log p not finite at (%d, %d, %d)"
                      " n = %d,\n", x, y, z, n) ;
            }
          }
        }
      }
    }
  }
  fflush(stdout) ;

  return((float)total_log_p) ;
}

#if 0
int
GCAsampleStats(GCA *gca, MRI *mri, int class,
               Real x, Real y, Real z,
               Real *pmean, Real *pvar, Real *pprior)
{
  int    xm, xp, ym, yp, zm, zp, width, height, depth, n ;
  Real   val, xmd, ymd, zmd, xpd, ypd, zpd ;  /* d's are distances */
  Real   prior, mean, var, wt ;
  GCAN   *gcan ;
  GC1D   *gc ;

  width = gca->node_width ;
  height = gca->node_height ;
  depth = gca->node_depth ;

  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) ;

  prior = mean = var = 0.0 ;


  gcan = &gca->nodes[xm][ym][zm] ;
  wt = xpd * ypd * zpd
       for (n = 0 ; n < gcan->nlabels ; n++)
       {
         if (gcan->labels[n] == class)
           break ;
       }
       if (n < gcan->nlabels)   /* found it */
       {
         gc = &gcan->gcs[n] ;
         prior += wt * gc->prior ;
       }

       gcan = &gca->nodes[xp][ym][zm] ;
  wt = xmd * ypd * zpd

       gcan = &gca->nodes[xm][yp][zm] ;
  wt = xpd * ymd * zpd ;

  gcan = &gca->nodes[xp][yp][zm] ;
  wt = xmd * ymd * zpd ;

  gcan = &gca->nodes[xm][ym][zp] ;
  wt = xpd * ypd * zmd ;

  gcan = &gca->nodes[xp][ym][zp] ;
  wt = xmd * ypd * zmd ;

  gcan = &gca->nodes[xm][yp][zp] ;
  wt = xpd * ymd * zmd ;

  gcan = &gca->nodes[xp][yp][zp] ;
  wt = xmd * ymd * zmd ;
  return(NO_ERROR) ;
}
#endif

#define MIN_SPACING   8

int
GCAtransformSamples(GCA *gca_src, GCA *gca_dst, GCA_SAMPLE *gcas, int nsamples)
{
  int       scale, i, label, xd, yd, zd, xs, ys, zs, n, xk, yk, zk,
  xd0, yd0, zd0, min_y_i, xdn, ydn, zdn ;
  float     max_p, min_v, vscale, min_y, prior ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  GC1D      *gc ;
  MRI       *mri_found ;

  vscale = 1 ;
  mri_found = MRIalloc(gca_dst->node_width*vscale,
                       gca_dst->node_height*vscale,
                       gca_dst->node_depth*vscale,
                       MRI_UCHAR) ;
  // change the voxel size
  mri_found->xsize = gca_dst->node_spacing*vscale;
  mri_found->ysize = gca_dst->node_spacing*vscale;
  mri_found->zsize = gca_dst->node_spacing*vscale;

  // copy direction cosines
  GCAcopyDCToMRI(gca_dst, mri_found);

  scale = gca_src->prior_spacing / gca_dst->prior_spacing ;
  min_y = 10000 ;
  min_y_i = -1 ;
  for (i = 0 ; i < nsamples ; i++)
  {
    label = gcas[i].label ;

    xs = gcas[i].xp ;
    ys = gcas[i].yp ;
    zs = gcas[i].zp ;
    xd0 = (int)(xs * scale) ;
    yd0 = (int)(ys * scale) ;
    zd0 = (int)(zs * scale) ;
    max_p = -1.0 ;  /* find appropriate label with highest prior in dst */
    min_v =  10000000.0f ;
    for (xk = -scale/2 ; xk <= scale/2 ; xk++)
    {
      xd = MIN(MAX(0, xd0+xk), gca_dst->prior_width-1) ;
      for (yk = -scale/2 ; yk <= scale/2 ; yk++)
      {
        yd = MIN(MAX(0, yd0+yk), gca_dst->prior_height-1) ;
        for (zk = -scale/2 ; zk <= scale/2 ; zk++)
        {
          zd = MIN(MAX(0, zd0+zk), gca_dst->prior_height-1) ;
          if (MRIvox(mri_found, (int)(xd*vscale), (int)(yd*vscale),
                     (int)(zd*vscale)))
            continue ;
          if (!GCApriorToNode(gca_dst, xd, yd, zd, &xdn, &ydn, &zdn))
          {
            gcan = &gca_dst->nodes[xdn][ydn][zdn] ;
            gcap = &gca_dst->priors[xd][yd][zd] ;
            if (gcap == NULL || gcap->nlabels <= 0)
              continue;
            for (n = 0 ; n < gcan->nlabels ; n++)
            {
              gc = &gcan->gcs[n] ;
              prior = getPrior(gcap, gcan->labels[n]) ;
              if (gcan->labels[n] == label &&
                  (prior > max_p
                   /*|| FEQUAL(prior,max_p) && gc->var < min_v)*/))
              {
                /*              min_v = gc->var ;*/
                max_p = prior ;
                gcas[i].xp = xd ;
                gcas[i].yp = yd ;
                gcas[i].zp = zd ;
              }
            }
          }
        }
      }
    }
    if (max_p < 0)
    {
      fprintf(stdout, "WARNING: label %d not found at (%d,%d,%d)\n",
              label, gcas[i].xp, gcas[i].yp, gcas[i].zp) ;
      DiagBreak() ;
    }
    MRIvox(mri_found, (int)(gcas[i].xp*vscale),
           (int)(gcas[i].yp*vscale), (int)(gcas[i].zp*vscale))= 1;
    if (gcas[i].yp < min_y)
    {
      min_y = gcas[i].yp ;
      min_y_i = i ;
    }
  }

  i = min_y_i ;
  fprintf(stdout, "min_y = (%d, %d, %d) at i=%d, label=%d\n",
          gcas[i].xp, gcas[i].yp, gcas[i].zp, i, gcas[i].label) ;
  MRIfree(&mri_found) ;
  fflush(stdout) ;
  return(NO_ERROR) ;
}

/* don't use a label with prior less than this */
#define MIN_MAX_PRIORS 0.5


static int exclude_classes[] =
  {
    0 /*1, 6, 21, 22, 23, 24, 25, 30, 57, 61, 62, 63*/
  } ;


#define TILES     3
#define MAX_PCT   .1

static int compare_gca_samples(const void *pc1, const void *pc2) ;

static int
compare_gca_samples(const void *pgcas1, const void *pgcas2)
{
  register GCA_SAMPLE *gcas1, *gcas2 ;

  gcas1 = (GCA_SAMPLE *)pgcas1 ;
  gcas2 = (GCA_SAMPLE *)pgcas2 ;

  /*  return(c1 > c2 ? 1 : c1 == c2 ? 0 : -1) ;*/
  if (FEQUAL(gcas1->prior, gcas2->prior))
  {
#if 0
    if (FEQUAL(gcas1->var, gcas2->var))
    {
      int  zv1, zv2 ;

      zv1 = gcas1->zn%10 ;
      zv2 = gcas2->zn%10 ;
      if (zv1 == zv2)
      {
        int  yv1, yv2 ;

        yv1 = gcas1->yn%10 ;
        zv2 = gcas2->yn%10 ;
        return(yv1-yv2) ;
      }

      return(zv1-zv2) ;
    }
#endif

#if 0
    if (gcas1->var > gcas2->var)
      return(1) ;
    else
      return(-1) ;
#else
    /* check size of determinants */
    return(1) ;
#endif
  }

  if (gcas1->prior > gcas2->prior)
    return(-1) ;
  else if (gcas1->prior < gcas2->prior)
    return(1) ;

  return(0) ;
}
#define MAX_SPACING   16  /* mm */

GCA_SAMPLE *
GCAfindStableSamplesByLabel(GCA *gca, int nsamples, float min_prior)
{
  GCA_SAMPLE *gcas, *ordered_labels[MAX_DIFFERENT_LABELS], *gcas2 ;
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  int        found[MAX_DIFFERENT_LABELS], x, y, z, width, height, depth, n,
  label, nfound, samples_added[MAX_DIFFERENT_LABELS] ;
  float      histo[MAX_DIFFERENT_LABELS], spacing, scale ;
  int        volume[TILES][TILES], max_class, total, extra, total_found,
  label_counts[MAX_DIFFERENT_LABELS],
  current_index[MAX_DIFFERENT_LABELS],
  ordered_label_counts[MAX_DIFFERENT_LABELS], i, index,
  *x_indices, *y_indices, *z_indices, nindices ;

  memset(histo, 0, sizeof(histo)) ;
  memset(label_counts, 0, sizeof(label_counts)) ;
  memset(samples_added, 0, sizeof(samples_added)) ;
  memset(current_index, 0, sizeof(current_index)) ;
  memset(ordered_label_counts, 0, sizeof(ordered_label_counts)) ;
  memset(found, 0, sizeof(found)) ;
  memset(volume, 0, sizeof(volume)) ;
  gcas = calloc(nsamples, sizeof(GCA_SAMPLE )) ;
  width = gca->prior_width ;
  height = gca->prior_height ;
  depth = gca->prior_depth ;

  /* compute the max priors and min variances for each class */
  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        gcap = &gca->priors[x][y][z] ;
        if (gcap==NULL)
          continue;
        for (n = 0 ; n < gcap->nlabels ; n++)
        {
          label = gcap->labels[n] ;
          if (label == Gdiag_no)
            DiagBreak() ;
          histo[label] +=
            (float)nint(gcap->total_training*gcap->priors[n]) ;
          label_counts[label]++ ;
        }
      }
    }
  }

  for (max_class = MAX_DIFFERENT_LABELS ; max_class >= 0 ; max_class--)
    if (histo[max_class] > 0)
      break ;
  fprintf(stdout, "max class = %d\n", max_class) ;

  /* count total # of samples */
  for (total = 0, n = 1 ; n <= max_class ; n++)
    total += histo[n] ;

  fprintf(stdout, "%d total training samples found\n", total) ;
  fflush(stdout) ;

  /* turn histogram into samples/class */
  for (n = 1 ; n <= max_class ; n++)
    histo[n] = (float)nint(0.25+histo[n]*(float)nsamples/total) ;

  for (n = 0 ; n < sizeof(exclude_classes)/sizeof(exclude_classes[0]) ; n++)
    histo[exclude_classes[n]] = 0 ;

  /* crop max # per class */
  for (n = 1 ; n <= max_class ; n++)
    if (histo[n] > nint(MAX_PCT*nsamples))
      histo[n] = nint(MAX_PCT*nsamples) ;

  for (extra = 0, n = 1 ; n <= max_class ; n++)
    extra += histo[n] ;
  extra = nsamples-extra ;  /* from rounding */
  printf("%d extra samples for redistribution...\n", extra) ;

  /* first add to classes with only one sample */
  for (n = 1 ; extra > 0 && n <= max_class ; n++)
  {
    if (histo[n] == 1)
    {
      histo[n]++ ;
      extra-- ;
    }
  }

  while (extra > 0)  /* add 1 to each class */
  {
    for (n = 1 ; extra > 0 && n <= max_class ; n++)
    {
      if (histo[n] >= 1)
      {
        histo[n]++ ;
        extra-- ;
      }
    }
  }

  {
    FILE *fp ;
    fp = fopen("classes.dat", "w") ;
    for (n = 1 ; n <= max_class ; n++)
      if (!FZERO(histo[n]))
        fprintf(fp, "%d  %2.1f\n", n, histo[n]) ;
    fclose(fp) ;
  }

  /* allocate arrays that will be used for sorting */
  for (n = 1 ; n <= max_class ; n++)
    if (histo[n] > 0)
    {
      ordered_labels[n] =
        (GCA_SAMPLE *)calloc(label_counts[n], sizeof(GCA_SAMPLE)) ;
      if (!ordered_labels[n])
        ErrorExit(ERROR_NO_MEMORY,
                  "GCAfindStableSamplesByLabel: "
                  "could not allocate %d samples "
                  "for label %d", label_counts[n], n) ;
    }


  nindices = width*height*depth ;
  x_indices = (int *)calloc(nindices, sizeof(int)) ;
  y_indices = (int *)calloc(nindices, sizeof(int)) ;
  z_indices = (int *)calloc(nindices, sizeof(int)) ;

  for (i = 0 ; i < nindices ; i++)
  {
    x_indices[i] = i % width ;
    y_indices[i] = (i/width) % height ;
    z_indices[i] = (i / (width*height)) % depth ;
  }
  for (i = 0 ; i < nindices ; i++)
  {
    int tmp ;
    index = (int)randomNumber(0.0, (double)(nindices-0.0001)) ;

    tmp = x_indices[index] ;
    x_indices[index] = x_indices[i] ;
    x_indices[i] = tmp ;

    tmp = y_indices[index] ;
    y_indices[index] = y_indices[i] ;
    y_indices[i] = tmp ;

    tmp = z_indices[index] ;
    z_indices[index] = z_indices[i] ;
    z_indices[i] = tmp ;
  }

  for (index = 0 ; index < nindices ; index++)
  {
    x = x_indices[index] ;
    y = y_indices[index] ;
    z = z_indices[index] ;
    gcap = &gca->priors[x][y][z] ;
    if (gcap==NULL)
      continue;
    for (n = 0 ; n < gcap->nlabels ; n++)
    {
      label = gcap->labels[n] ;
      gc = GCAfindPriorGC(gca, x, y, z, label) ;
      if (histo[label] > 0)
      {
        i = ordered_label_counts[label] ;
        ordered_label_counts[label]++ ;
        ordered_labels[label][i].means =
          (float *)calloc(gca->ninputs, sizeof(float)) ;
        ordered_labels[label][i].covars =
          (float *)calloc((gca->ninputs*(gca->ninputs+1))/2,
                          sizeof(float)) ;
        if (!ordered_labels[label][i].means
            || !ordered_labels[label][i].covars)
          ErrorExit(ERROR_NOMEMORY,
                    "could not allocate mean (%d) and "
                    "covariance (%d) matrices",
                    gca->ninputs, gca->ninputs*(gca->ninputs+1)/2) ;
        ordered_labels[label][i].xp = x ;
        ordered_labels[label][i].yp = y ;
        ordered_labels[label][i].zp = z ;
        ordered_labels[label][i].label = label ;
        ordered_labels[label][i].prior = getPrior(gcap, label) ;
        if (!gc)
        {
          int  r, c, v ;
          for (v = r = 0 ; r < gca->ninputs ; r++)
          {
            ordered_labels[label][i].means[r] = 0.0 ;
            for (c = r ; c < gca->ninputs ; c++, v++)
            {
              if (c==r)
                ordered_labels[label][i].covars[v] = 1.0 ;
              else
                ordered_labels[label][i].covars[v] = 0 ;
            }
          }
        }
        else
        {
          int  r, c, v ;
          for (v = r = 0 ; r < gca->ninputs ; r++)
          {
            ordered_labels[label][i].means[r] = gc->means[r] ;
            for (c = r ; c < gca->ninputs ; c++, v++)
            {
              ordered_labels[label][i].covars[v] = gc->covars[v] ;
            }
          }
        }
      }
    }
  }

  free(x_indices) ;
  free(y_indices) ;
  free(z_indices) ;

  /* sort each list of labels by prior, then by variance */
  for (n = 1 ; n <= max_class ; n++)
    if (histo[n] > 0)
    {
      qsort(ordered_labels[n], ordered_label_counts[n], sizeof(GCA_SAMPLE),
            compare_gca_samples) ;
    }

  total_found = nfound = 0 ;

  for (spacing = MAX_SPACING ; spacing >= gca->node_spacing ; spacing /= 2)
  {
    MRI  *mri_found ;
    int  xv, yv, zv ;

    for (n = 1 ; n <= max_class ; n++)
      current_index[n] = 0 ;

    scale = gca->prior_spacing / spacing ;
    mri_found = MRIalloc(width*scale, height*scale, depth*scale, MRI_UCHAR);

    // change the size
    mri_found->xsize = gca->prior_spacing*scale;
    mri_found->ysize = gca->prior_spacing*scale;
    mri_found->zsize = gca->prior_spacing*scale;

    GCAcopyDCToMRI(gca, mri_found);

    for (i = 0 ; i < total_found ; i++)
    {
      xv = gcas[i].xp*scale ;
      yv = gcas[i].yp*scale ;
      zv = gcas[i].zp*scale ;
      MRIvox(mri_found, xv, yv, zv) = 1 ;
    }

    do
    {

      nfound = 0 ;
      for (n = 1 ; n <= max_class ; n++)
      {
        if (samples_added[n] < histo[n])
          /* add another sample from this class*/
        {
          while ((ordered_labels[n][current_index[n]].label < 0) &&
                 current_index[n] < ordered_label_counts[n])
            current_index[n]++ ;
          if (current_index[n] < ordered_label_counts[n])
          {
            gcas2 = &ordered_labels[n][current_index[n]] ;
            current_index[n]++ ;
            xv = gcas2->xp*scale ;
            yv = gcas2->yp*scale ;
            zv = gcas2->zp*scale;

            if (n == Gdiag_no)
              DiagBreak() ;

            if (!MRIvox(mri_found, xv, yv, zv))
              /* none at this location yet */
            {
              if (gcas2->label == Gdiag_no)
                DiagBreak() ;
              samples_added[n]++ ;
              gcas[total_found+nfound++] = *gcas2 ;
              MRIvox(mri_found, xv, yv, zv) = gcas2->label ;
              gcas2->label = -1 ;
              if (nfound+total_found >= nsamples)
                break ;
            }
          }
        }
      }

      total_found += nfound ;
    }
    while ((nfound > 0) && total_found < nsamples) ;
    MRIfree(&mri_found) ;
  }

  if (total_found != nsamples)
  {
    ErrorPrintf(ERROR_BADPARM,
                "could only find %d samples!\n", total_found) ;
  }
  return(gcas) ;
}


GCA_SAMPLE *
GCAfindContrastSamples(GCA *gca, int *pnsamples, int min_spacing,
                       float min_prior)
{
#if 0
  GCA_SAMPLE *gcas ;
  GC1D       *gc ;
  int        x, y, z, width, height, depth, label, nfound, prior_stride,
  label_counts[MAX_DIFFERENT_LABELS], best_label, nzeros,
  xi, yi, zi, xk, yk, zk, i, j, k, labels[3][3][3], found ;
  float      max_prior, total_mean,
  priors[3][3][3][MAX_DIFFERENT_LABELS],
  best_means[MAX_GCA_INPUTS], best_sigma,
  means[3][3][3][MAX_DIFFERENT_LABELS][MAX_GCA_INPUTS], mean, sigma,
  vars[3][3][3][MAX_DIFFERENT_LABELS] ;

  memset(label_counts, 0, sizeof(label_counts)) ;
  width = gca->prior_width ;
  height = gca->prior_height ;
  depth = gca->prior_depth ;
  gcas = calloc(width*height*depth, sizeof(GCA_SAMPLE)) ;

  prior_stride = min_spacing / gca->node_spacing ;

  total_mean = 0.0 ;

  /* compute the max priors and min variances for each class */
  for (nzeros = nfound = x = 0 ; x < width ; x += prior_stride)
  {
    for (y = 0 ; y < height ; y += prior_stride)
    {
      for (z = 0 ; z < depth ; z += prior_stride)
      {
        if (abs(x-31)<=prior_stride &&
            abs(y-22)<=prior_stride &&
            abs(z-36)<=prior_stride)
          DiagBreak() ;
        for (xk = -1 ; xk <= 1 ; xk++)
        {
          xi = x+xk ;
          i = xk+1 ;
          if (xi < 0 || xi >= width)
            continue ;
          for (yk = -1 ; yk <= 1 ; yk++)
          {
            yi = y+yk ;
            j = yk + 1 ;
            if (yi < 0 || yi >= height)
              continue ;
            for (zk = -1 ; zk <= 1 ; zk++)
            {
              zi = z+zk ;
              k = zk+1 ;
              if (zi < 0 || zi >= depth)
                continue ;
              gcaRegionStats(gca, xi, yi, zi,
                             prior_stride/3,
                             priors[i][j][k],
                             vars[i][j][k],
                             means[i][j][k]) ;
              if (priors[i][j][k][4] > .5*min_prior ||
                  /* left lat ven */
                  priors[i][j][k][5] > .5*min_prior ||
                  /* inf left lat ven */
                  priors[i][j][k][14] > .5*min_prior ||
                  /* 3rd ven */
                  priors[i][j][k][15] > .5*min_prior ||
                  /* 4th ven */
                  priors[i][j][k][43] > .5*min_prior ||
                  priors[i][j][k][44] > .5*min_prior)
                DiagBreak() ;
              max_prior = 0 ;

              /* find highest prior label */
              for (label = 0 ;
                   label < MAX_DIFFERENT_LABELS ;
                   label++)
              {
                if (priors[i][j][k][label] > max_prior)
                {
                  max_prior = priors[i][j][k][label] ;
                  labels[i][j][k] = label ;
                }
              }
            }
          }
        }

        /* search nbrhd for high-contrast stable label */
        best_label = labels[1][1][1] ;
        found = 0 ;
        best_sigma = sqrt(vars[1][1][1][best_label]) ;
        for (r = 0 ; r < gca->ninputs ; r++)
          best_means[r] = means[1][1][1][best_label][r] ;

        gc = gcaFindHighestPriorGC(gca, x, y, z,
                                   best_label,prior_stride/3);
        if (!gc || get_node_prior(gca, best_label, x, y, z) < min_prior)
          continue ;

        for (xk = -1 ; xk <= 1 ; xk++)
        {
          xi = x+xk ;
          i = xk+1 ;
          if (xi < 0 || xi >= width)
            continue ;
          for (yk = -1 ; yk <= 1 ; yk++)
          {
            yi = y+yk ;
            j = yk + 1 ;
            if (yi < 0 || yi >= height)
              continue ;
            for (zk = -1 ; zk <= 1 ; zk++)
            {
              zi = z+zk ;
              k = zk+1 ;
              if (zi < 0 || zi >= depth)
                continue ;
              if (i == 1 && j == 1 && k == 1)
                continue ;
              label = labels[i][j][k] ;
              if (priors[i][j][k][label]
                  < min_prior || label == best_label)
                continue ;

              mean = means[i][j][k][label] ;
              sigma = sqrt(vars[i][j][k][label]) ;
              gc =
                gcaFindHighestPriorGC(gca, xi, yi, zi,
                                      label,prior_stride/3);
              if (!gc || get_node_prior(gca, label,
                                        xi, yi, zi) < min_prior)
                continue ;
              if (abs(best_mean - mean) > 4*abs(best_sigma+sigma))
              {
#if 0
                if (gcaFindBestSample(gca, xi, yi, zi,
                                      label, prior_stride/3,
                                      &gcas[nfound]) == NO_ERROR)
#else
                if (gcaFindClosestMeanSample(gca, mean,
                                             min_prior,
                                             xi, yi, zi,
                                             label,
                                             prior_stride/3,
                                             &gcas[nfound]) ==
                    NO_ERROR)
#endif
                {
                  if (label == Gdiag_no)
                    DiagBreak() ;
                  found = 1 ;
                  if (label > 0)
                    total_mean += means[i][j][k][label] ;
                  else
                    nzeros++ ;
                  label_counts[label]++ ;
                  nfound++ ;
                }
              }
            }
          }
        }

        if (found)   /* add central label */
        {
          if (best_label == Gdiag_no)
            DiagBreak() ;
#if 0
          if (gcaFindBestSample(gca, x, y, z,
                                best_label, prior_stride/3,
                                &gcas[nfound]) == NO_ERROR)
#else
          if (gcaFindClosestMeanSample(gca, best_mean,
                                       min_prior, x, y, z,
                                       best_label, prior_stride/3,
                                       &gcas[nfound]) ==
              NO_ERROR)
#endif
          {
            if (best_label > 0)
            {
              for (r = 0 ; r < gca->ninputs ; r++)
                total_means[r] += means[1][1][1][best_label][r] ;
            }
            else
              nzeros++ ;
            label_counts[best_label]++ ;
            nfound++ ;
          }
        }
      }
    }
  }

  fprintf(stdout, "total sample mean = %2.1f (%d zeros)\n",
          total_mean/((float)nfound-nzeros), nzeros) ;

  {
    int  n ;
    FILE *fp ;

    fp = fopen("classes.dat", "w") ;
    for (n = 0 ; n < MAX_DIFFERENT_LABELS ; n++)
      if (label_counts[n] > 0)
        fprintf(fp, "%d  %d\n", n, label_counts[n]) ;
    fclose(fp) ;
  }

  *pnsamples = nfound ;
  fflush(stdout) ;

  return(gcas) ;
#else
  ErrorReturn(NULL, (ERROR_UNSUPPORTED,
                     "GCAfindContrastSamples: no longer supported")) ;
#endif
}


GCA_SAMPLE *
GCAfindStableSamples(GCA *gca,
                     int *pnsamples, int min_spacing,
                     float min_prior, int *exclude_list,
                     int unknown_nbr_spacing)
{
  GCA_SAMPLE *gcas ;
  int        xi, yi, zi, width, height, depth, label, nfound,
  label_counts[MAX_DIFFERENT_LABELS], best_label, nzeros, r ;
  float      max_prior, total_means[MAX_GCA_INPUTS],
  /*mean_dist,*/ best_mean_dist,
  priors[MAX_DIFFERENT_LABELS], means[MAX_DIFFERENT_LABELS][MAX_GCA_INPUTS],
  vars[MAX_DIFFERENT_LABELS], max_priors[MAX_DIFFERENT_LABELS],
  prior_stride, x, y, z, min_unknown[MAX_GCA_INPUTS],\
  max_unknown[MAX_GCA_INPUTS], prior_factor ;
  MRI        *mri_filled ;

#define MIN_UNKNOWN_DIST  2

  gcaFindMaxPriors(gca, max_priors) ;
  gcaFindIntensityBounds(gca, min_unknown, max_unknown) ;
  printf("bounding unknown intensity as < ") ;
  for (r = 0 ; r < gca->ninputs ; r++)
    printf("%2.1f ", min_unknown[r]) ;
  printf("or > ") ;
  for (r = 0 ; r < gca->ninputs ; r++)
    printf("%2.1f ", max_unknown[r]) ;
  printf("\n") ;

  memset(label_counts, 0, sizeof(label_counts)) ;

  width = gca->prior_width ;
  height = gca->prior_height ;
  depth = gca->prior_depth ;

  // samples size allocated is prior voxel points
  gcas = calloc(width*height*depth, sizeof(GCA_SAMPLE)) ;
  if (!gcas)
    ErrorExit(ERROR_NOMEMORY, "%s: could not allocate %dK x %d samples\n",
              "GCAfindStableSamples",
              width*height*depth/1024, sizeof(GCA_SAMPLE)) ;

  // create a full size volume prior_width*prior_spacing = image width
  mri_filled = MRIalloc(width*gca->prior_spacing,height*gca->prior_spacing,
                        depth*gca->prior_spacing,MRI_UCHAR);
  // use the mri_prior_ header

  mri_filled->xsize = gca->xsize;
  mri_filled->ysize = gca->ysize;
  mri_filled->zsize = gca->zsize;

  GCAcopyDCToMRI(gca, mri_filled);

  prior_stride = (float)min_spacing / (float)gca->prior_spacing ;
  if (prior_stride >= 2.5)
    prior_factor = .5 ;
  else if (prior_stride >= 1.5)
    prior_factor = .75 ;
  else
    prior_factor = .9 ;

  for (r = 0 ; r < gca->ninputs ; r++)
    total_means[r] = 0.0 ;

  /* compute the max priors and min variances for each class */
  for (nzeros = nfound = x = 0 ; x < width ; x += prior_stride)
  {
    xi = nint(x) ;
    for (y = 0 ; y < height ; y += prior_stride)
    {
      yi = nint(y) ;
      for (z = 0 ; z < depth ; z += prior_stride)
      {
        zi = nint(z) ;
        if (xi == Gx && yi == Gy && zi == Gz)
          DiagBreak() ;
        if (abs(x-31)<=prior_stride &&
            abs(y-22)<=prior_stride &&
            abs(z-36)<=prior_stride)
          DiagBreak() ;
        gcaRegionStats(gca, x, y, z,
                       prior_stride/2, priors, vars, means) ;

        ///////////// diag code /////////////////////////////////////
        if (priors[4] > .5*min_prior ||  /* left lat ven */
            priors[5] > .5*min_prior ||  /* inf left lat ven */
            priors[14] > .5*min_prior ||  /* 3rd ven */
            priors[15] > .5*min_prior ||  /* 4th ven */
            priors[43] > .5*min_prior ||
            priors[44] > .5*min_prior)
          DiagBreak() ;
        /////////////////////////////////////////////////////////////

        best_mean_dist = 0.0 ;
        max_prior = -1 ;

        if ((different_nbr_max_labels(gca, x, y, z,
                                      unknown_nbr_spacing, 0) > 0) &&
            (exclude_list && exclude_list[0] == 0) &&
            (priors[0] >= min_prior) &&
            (priors[0] >= .5*max_priors[0]))
          best_label = 0 ;
        else
          best_label = -1 ;

        for (label = 1 ; label < MAX_DIFFERENT_LABELS ; label++)
        {
          /* ignore it if:
             1. It's prior is less than min_prior and
             it's prior is less than the max for this class.
             2. It's prior is less than 1/2 of min prior regardless.
          */
          if (((priors[label] < min_prior) && \
               (priors[label] < prior_factor*max_priors[label])) ||
              (priors[label] < .5*min_prior) ||
              (exclude_list && exclude_list[label] > 0))
            continue ;

          if ((best_label == 0) ||
              (priors[label] > max_prior) ||
              (FEQUAL(priors[label], max_prior) &&
               label_counts[best_label] > label_counts[label]))
          {
            best_label = label ;
            max_prior = priors[label] ;
          }
        }
#if 1
        if (nfound > 0)
        {
          double p = randomNumber(0, 1.0) ;
          if (p < ((double)label_counts[best_label] / nfound))
            continue ;
        }
#endif
        if (best_label >= 0)
        {
          if (best_label == 0)
          {
#if 1
            if (gcaFindBestSample(gca, x, y, z,
                                  best_label, prior_stride/2,
                                  &gcas[nfound]) == NO_ERROR)
#else
            gcaFindClosestMeanSample(gca, 255,
                                     min_prior, x, y, z, 0,
                                     prior_stride/2,
                                     &gcas[nfound]);
            if (means[0] > 100)
#endif
            {
              int  xv, yv, zv ;

              if (((means[0] <= min_unknown) ||
                   (means[0] >= max_unknown))) /* disabled check */
              {
                if (!GCApriorToVoxel(gca, mri_filled,
                                     x, y, z, &xv, &yv, &zv))
                {
                  if (MRIvox(mri_filled, xv, yv, zv) == 0)
                  {
                    mriFillRegion(mri_filled,
                                  xv, yv, zv, 1,
                                  MIN_UNKNOWN_DIST) ;
                    /* MRIvox(mri_filled, xv, yv, zv) = 1 ;*/
                    nzeros++ ;
                    label_counts[best_label]++ ;
                    nfound++ ;
                  }
                }
              }
              else
                DiagBreak() ;
            }
          }
          else
          {
            /* find best gc with this label */
            if (gcaFindBestSample(gca, x, y, z,
                                  best_label, prior_stride/2,
                                  &gcas[nfound]) == NO_ERROR)
            {
              for (r = 0 ; r < gca->ninputs ; r++)
                total_means[r] += means[best_label][r] ;
              label_counts[best_label]++ ;
              nfound++ ;
            }
          }
        }
      }
    }
  }


  fprintf(stdout, "total sample mean = %2.1f (%d zeros)\n",
          total_means[0]/((float)nfound-nzeros), nzeros) ;

  if (getenv("GCA_WRITE_CLASS"))
  {
    int  n ;
    FILE *fp ;

    fp = fopen("classes.dat", "w") ;
    if (fp)
    {
      for (n = 0 ; n < MAX_DIFFERENT_LABELS ; n++)
        if (label_counts[n] > 0)
          fprintf(fp, "%d  %d\n", n, label_counts[n]) ;
      fclose(fp) ;
    }
  }

  *pnsamples = nfound ;
  fflush(stdout) ;

  MRIfree(&mri_filled) ;
  return(gcas) ;
}

//////////////////////////////////////////////////////////////////
// write segmented volume for sampled points
// the values are set only at prior_spacing/size
int
GCAwriteSamples(GCA *gca, MRI *mri, GCA_SAMPLE *gcas, int nsamples,
                char *fname)
{
  int    n, xv, yv, zv ;
  MRI    *mri_dst ;

  mri_dst = MRIalloc(mri->width, mri->height, mri->depth, MRI_UCHAR) ;
  // add header information
  MRIcopyHeader(mri, mri_dst);
  GCAcopyDCToMRI(gca, mri_dst);

  // for each sample
  for (n = 0 ; n < nsamples ; n++)
  {
    // use the gcas to get prior value to get the volume position
    if (!GCApriorToVoxel(gca, mri_dst, gcas[n].xp, gcas[n].yp, gcas[n].zp,
                         &xv, &yv, &zv))
    {
      if (gcas[n].label > 0) // if label non-zero (!unknown)
        MRIsetVoxVal(mri_dst, xv, yv, zv, 0, gcas[n].label) ;
      else                                  // unknown label changed to
        MRIsetVoxVal(mri_dst, xv, yv, zv, 0, Left_undetermined) ;
      /* Left undetermined - to make it visible */

      /////////////// diagnostics //////////////////////////////////////
      if (DIAG_VERBOSE_ON && Gdiag & DIAG_SHOW)
        printf("label %d: (%d, %d, %d) <-- (%d, %d, %d)\n",
               gcas[n].label,gcas[n].xp,gcas[n].yp,gcas[n].zp,
               xv, yv, zv) ;
      /////////////////////////////////////////////////////////////////
    }
  }
  MRIwrite(mri_dst, fname) ;
  MRIfree(&mri_dst) ;
  return(NO_ERROR) ;
}

MRI *
GCAmri(GCA *gca, MRI *mri)
{
  int       frame, width, height, depth, x, y, z, xp, yp, zp, n, xn, yn, zn ;
  float     val ;
  GC1D      *gc ;
  GCA_PRIOR *gcap ;

  if (!mri)
  {
    mri = MRIallocSequence(gca->node_width,
                           gca->node_height,
                           gca->node_depth,
                           MRI_UCHAR,
                           gca->ninputs) ;
    mri->xsize = gca->xsize*gca->node_spacing;
    mri->ysize = gca->ysize*gca->node_spacing;
    mri->zsize = gca->zsize*gca->node_spacing;
  }
  // in order to create the gca volume,
  // the volume must have the same direction cosines
  GCAcopyDCToMRI(gca, mri);

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

  for (frame = 0 ; frame < gca->ninputs ; frame++)
  {
    for (x = 0 ; x < width ; x++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (z = 0 ; z < depth ; z++)
        {
          if (!GCAvoxelToPrior(gca, mri, x, y, z, &xp, &yp, &zp))
            if (!GCAvoxelToNode(gca, mri, x, y, z, &xn, &yn, &zn))
            {
              gcap = &gca->priors[xp][yp][zp] ;
              if (gcap==NULL)
                continue;
              for (val = 0.0, n = 0 ; n < gcap->nlabels ; n++)
              {
                gc = GCAfindGC(gca, xn, yn, zn, gcap->labels[n]) ;
                if (gc)
                  val += gc->means[frame] * gcap->priors[n] ;
              }
              MRIsetVoxVal(mri, x, y, z, frame, val) ;
            }
        }
      }
    }
  }
  return(mri) ;
}

MRI *
GCAlabelMri(GCA *gca, MRI *mri, int label, TRANSFORM *transform)
{
  int      width, height, depth, x, y, z, xn, yn, zn, n, frame ;
  float    val ;
  GC1D     *gc = NULL ;
  GCA_NODE *gcan ;
  MRI      *mri_norm ;

  if (!mri)
  {
    mri = MRIallocSequence(gca->node_width,
                           gca->node_height,
                           gca->node_depth,
                           MRI_UCHAR,
                           gca->ninputs) ;

    mri->xsize = gca->xsize*gca->node_spacing;
    mri->ysize = gca->ysize*gca->node_spacing;
    mri->zsize = gca->zsize*gca->node_spacing;
    GCAcopyDCToMRI(gca, mri);
    // mri=NULL, then use the gca volume
    // mri!=NULL, then use the volume as it is to write the label
  }
  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;
  mri_norm = MRIalloc(width, height, depth, MRI_SHORT) ;
  MRIcopyHeader(mri, mri_norm);

  for (frame = 0 ; frame < gca->ninputs ; frame++)
  {
    MRIclear(mri_norm) ;
    for (x = 0 ; x < width ; x++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (z = 0 ; z < depth ; z++)
        {
          if (x == width/2 && y == height/2 && z == depth/2)
            DiagBreak() ;
          if (x == 19 && y == 14 && z == 15)
            DiagBreak() ;

          if (!GCAsourceVoxelToNode(gca, mri, transform,
                                    x, y, z, &xn, &yn, &zn))
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            if (gcan->nlabels < 1)
              continue ;
            for (n = 0 ; n < gcan->nlabels ; n++)
            {
              gc = &gcan->gcs[n] ;
              if (gcan->labels[n] == label)
                break ;
            }
            if (n >= gcan->nlabels)
              continue ;
            val = gc->means[frame] ;
            switch (mri->type)
            {
            default:
              ErrorReturn(NULL,
                          (ERROR_UNSUPPORTED,
                           "GCAlabelMri: unsupported image"
                           " type %d",
                           mri->type)) ;
            case MRI_UCHAR:
              MRIseq_vox(mri, x, y, z,frame) = (unsigned short)val ;
              break ;
            case MRI_SHORT:
              MRISseq_vox(mri, x, y, z,frame) = (short)val ;
              break ;
            case MRI_FLOAT:
              MRIFseq_vox(mri, x, y, z, frame) = (float)val ;
              break ;
            }
            MRISvox(mri_norm, x, y, z) += 1 ;
          }
        }
      }
    }
    for (x = 0 ; x < width ; x++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (z = 0 ; z < depth ; z++)
        {
          if (MRISvox(mri_norm, x, y, z) > 0)
            MRISseq_vox(mri, x, y, z, frame) =
              nint((float)MRISseq_vox(mri,x,y,z,frame)/ \
                   (float)MRISvox(mri_norm,x,y,z)) ;
        }
      }
    }
  }
  MRIfree(&mri_norm) ;
  return(mri) ;
}

static int
different_nbr_max_labels(GCA *gca, int x, int y, int z, int wsize, int label)
{
  int      xk, yk, zk, xi, yi, zi, nbrs, n, width, height, depth, max_label ;
  GCA_PRIOR *gcap ;
  double     max_p ;

  // look at prior volume
  width = gca->prior_width ;
  height = gca->prior_height ;
  depth = gca->prior_depth ;
  for (nbrs = 0, xk = -wsize ; xk <= wsize ; xk++)
  {
    xi = x+xk ;
    if (xi < 0 || xi >= width)
      continue ;

    for (yk = -wsize ; yk <= wsize ; yk++)
    {
      yi = y+yk ;
      if (yi < 0 || yi >= height)
        continue ;

      for (zk = -wsize ; zk <= wsize ; zk++)
      {
        zi = z+zk ;
        if (zi < 0 || zi >= depth)
          continue ;

        gcap = &gca->priors[xi][yi][zi] ;
        if (gcap == NULL)
          continue;
        if (gcap->nlabels == 0) // no priors nor labels exist
          continue;

        max_p = gcap->priors[0] ;
        max_label = gcap->labels[0] ;
        for (n = 1 ; n < gcap->nlabels ; n++)
          if (gcap->priors[n] >= max_p)
          {
            max_label = gcap->labels[n] ;
            max_p = gcap->priors[n] ;
          }
        if (max_label != label)
          // if the label is different from the one given
          nbrs++ ;              // count them
      }
    }
  }
  return(nbrs) ;
}
#if 0
static int
different_nbr_labels(GCA *gca,
                     int x, int y, int z,
                     int wsize, int label, float pthresh)
{
  int      xk, yk, zk, xi, yi, zi, nbrs, n, width, height, depth ;
  GCA_PRIOR *gcap ;

  width = gca->prior_width ;
  height = gca->prior_height ;
  depth = gca->prior_depth ;
  for (nbrs = 0, xk = -wsize ; xk <= wsize ; xk++)
  {
    xi = x+xk ;
    if (xi < 0 || xi >= width)
      continue ;

    for (yk = -wsize ; yk <= wsize ; yk++)
    {
      yi = y+yk ;
      if (yi < 0 || yi >= height)
        continue ;

      for (zk = -wsize ; zk <= wsize ; zk++)
      {
        zi = z+zk ;
        if (zi < 0 || zi >= depth)
          continue ;

        gcap = &gca->priors[xi][yi][zi] ;
        if (gcap==NULL)
          continue;
        for (n = 0 ; n < gcap->nlabels ; n++)
          if ((gcap->labels[n] != label) && (gcap->priors[n] >= pthresh))
            nbrs++ ;
      }
    }
  }
  return(nbrs) ;
}
#endif
static int
gcaRegionStats(GCA *gca, int x, int y, int z, int wsize,
               float *priors, float *vars,
               float means[MAX_DIFFERENT_LABELS][MAX_GCA_INPUTS])
{
  int        xk, yk, zk, xi, yi, zi, width, height, depth, label, n,
  total_training, r, l ;
  GCA_PRIOR  *gcap  ;
  GC1D       *gc ;
  float      dof ;

  if (x == 28 && y == 24 && z == 36)
    DiagBreak() ;

  memset(priors, 0, MAX_DIFFERENT_LABELS*sizeof(float)) ;
  memset(vars, 0, MAX_DIFFERENT_LABELS*sizeof(float)) ;
  for (l = 0 ; l < MAX_DIFFERENT_LABELS ; l++)
    for (r = 0 ; r < gca->ninputs ; r++)
      means[l][r] = 0 ;

  width = gca->prior_width ;
  height = gca->prior_height ;
  depth = gca->prior_depth ;
  total_training = 0 ;
  for (xk = -wsize ; xk <= wsize ; xk++)
  {
    xi = x+xk ;
    if (xi < 0 || xi >= width)
      continue ;

    for (yk = -wsize ; yk <= wsize ; yk++)
    {
      yi = y+yk ;
      if (yi < 0 || yi >= height)
        continue ;

      for (zk = -wsize ; zk <= wsize ; zk++)
      {
        zi = z+zk ;
        if (zi < 0 || zi >= depth)
          continue ;

        gcap = &gca->priors[xi][yi][zi] ;
        if (gcap==NULL)
          continue;
        total_training += gcap->total_training ;

        for (n = 0 ; n < gcap->nlabels ; n++)
        {
          label = gcap->labels[n] ;
          if (label == Gdiag_no)
            DiagBreak() ;
          gc = GCAfindPriorGC(gca, xi, yi, zi, label) ;
          if (gc)
          {
            dof = (gcap->priors[n] * (float)gcap->total_training) ;

            vars[label] +=
              dof * covariance_determinant(gc, gca->ninputs) ;
            for (r = 0 ; r < gca->ninputs ; r++)
              means[label][r] += dof * gc->means[r] ;
            priors[label] += dof ;
          }
        }
      }
    }
  }

  for (label = 0 ; label < MAX_DIFFERENT_LABELS ; label++)
  {
    dof = priors[label] ;
    if (dof > 0)
    {
      vars[label] /= dof ;
      priors[label] /= total_training ;
      for (r = 0 ; r < gca->ninputs ; r++)
        means[label][r] /= dof ;
    }
  }
  return(NO_ERROR) ;
}
static int
gcaFindBestSample(GCA *gca, int x, int y, int z,int label,int wsize,
                  GCA_SAMPLE *gcas)
{
  int        xk, yk, zk, xi, yi, zi, width, height, depth, n,
  best_n, best_x, best_y, best_z, xn, yn, zn, r, c, v ;
  GCA_PRIOR  *gcap ;
  GC1D       *gc, *best_gc ;
  float      max_prior, min_var, prior ;

  width = gca->prior_width ;
  height = gca->prior_height ;
  depth = gca->prior_depth ;
  max_prior = 0.0 ;
  min_var = 10000.0f ;
  best_gc = NULL ;
  best_x = best_y = best_z = -1 ;
  best_n = -1 ;
  for (xk = -wsize ; xk <= wsize ; xk++)
  {
    xi = x+xk ;
    if (xi < 0 || xi >= width)
      continue ;

    for (yk = -wsize ; yk <= wsize ; yk++)
    {
      yi = y+yk ;
      if (yi < 0 || yi >= height)
        continue ;

      for (zk = -wsize ; zk <= wsize ; zk++)
      {
        zi = z+zk ;
        if (zi < 0 || zi >= depth)
          continue ;

        gcap = &gca->priors[xi][yi][zi] ;
        if (gcap==NULL)
          continue;
        for (n = 0 ; n < gcap->nlabels ; n++)
        {
          if (gcap->labels[n] != label)
            continue ;
          DiagBreak() ;
          prior = gcap->priors[n] ;
          if (!GCApriorToNode(gca, xi, yi, zi, &xn, &yn, &zn))
          {
            gc = GCAfindGC(gca, xn, yn, zn, label) ;
            if (!gc)
              continue ;

            if (prior > max_prior
#if 0
                ||
                (FEQUAL(prior, max_prior) && (gc->var < min_var)) ||
                (label == 0 && gc->mean > best_gc->mean)
#endif
               )
            {
              max_prior = prior ; /*min_var = gc->var ;*/
              best_gc = gc ;
              best_x = xi ;
              best_y = yi ;
              best_z = zi ;
              best_n = n ;
            }
          }
        }
      }
    }
  }

  if (best_x < 0)
  {
    ErrorPrintf(ERROR_BADPARM,
                "could not find GC1D for label %d at (%d,%d,%d)\n",
                label, x, y, z) ;
    return(ERROR_BADPARM) ;
  }
  if (best_x == 145/4 && best_y == 89/4 && best_z == 125/4)
    DiagBreak() ;
  gcas->xp = best_x ;
  gcas->yp = best_y ;
  gcas->zp = best_z ;
  gcas->label = label ;
  gcas->means = (float *)calloc(gca->ninputs, sizeof(float)) ;
  gcas->covars = (float *)calloc((gca->ninputs*(gca->ninputs+1))/2,
                                 sizeof(float)) ;
  if (!gcas->means || !gcas->covars)
    ErrorExit(ERROR_NOMEMORY,
              "GCAfindStableSamples: could not allocate "
              "mean (%d) and covariance (%d) matrices",
              gca->ninputs, gca->ninputs*(gca->ninputs+1)/2) ;
  for (r = v = 0 ; r < gca->ninputs ; r++)
  {
    gcas->means[r] = best_gc->means[r] ;
    for (c = r ; c < gca->ninputs ; c++, v++)
      gcas->covars[v] = best_gc->covars[v] ;
  }
  gcas->prior = max_prior ;

  return(NO_ERROR) ;
}
#if 0
static GC1D *
gcaFindHighestPriorGC(GCA *gca, int x, int y, int z,int label,int wsize)
{
  int        xk, yk, zk, xi, yi, zi, width, height, depth, n ;
  GCA_PRIOR  *gcap  ;
  GC1D       *best_gc ;
  float      max_prior, prior ;

  width = gca->prior_width ;
  height = gca->prior_height ;
  depth = gca->prior_depth ;
  max_prior = 0.0 ;
  best_gc = NULL ;
  for (xk = -wsize ; xk <= wsize ; xk++)
  {
    xi = x+xk ;
    if (xi < 0 || xi >= width)
      continue ;

    for (yk = -wsize ; yk <= wsize ; yk++)
    {
      yi = y+yk ;
      if (yi < 0 || yi >= height)
        continue ;

      for (zk = -wsize ; zk <= wsize ; zk++)
      {
        zi = z+zk ;
        if (zi < 0 || zi >= depth)
          continue ;

        gcap = &gca->priors[xi][yi][zi] ;
        if (gcap == NULL || gcap->nlabels <= 0)
          continue;
        for (n = 0 ; n < gcap->nlabels ; n++)
        {
          if (gcap->labels[n] != label)
            continue ;
          prior = gcap->priors[n] ;
          if (prior > max_prior)
          {
            max_prior = prior ;
            best_gc = GCAfindPriorGC(gca, x, y, z, label) ;
          }
        }
      }
    }
  }

  if (best_gc == NULL)
  {
    ErrorPrintf(ERROR_BADPARM,
                "could not find GC for label %d at (%d,%d,%d)\n",
                label, x, y, z) ;
    return(NULL) ;
  }

  return(best_gc) ;
}
static int
gcaFindClosestMeanSample(GCA *gca,
                         float *means, float min_prior,
                         int x, int y, int z, int label,
                         int wsize, GCA_SAMPLE *gcas)
{
  int        xk, yk, zk, xi, yi, zi, width, height, depth, n,
  best_x, best_y, best_z, r, c, v ;
  GCA_NODE   *gcan  ;
  GC1D       *gc, *best_gc ;
  float      min_dist, best_prior, prior, dist ;

  width = gca->node_width ;
  height = gca->node_height ;
  depth = gca->node_depth ;
  min_dist = 1000000.0f ;
  best_gc = NULL ;
  best_prior = 0.0 ;
  best_x = best_y = best_z = -1 ;
  for (xk = -wsize ; xk <= wsize ; xk++)
  {
    xi = x+xk ;
    if (xi < 0 || xi >= width)
      continue ;

    for (yk = -wsize ; yk <= wsize ; yk++)
    {
      yi = y+yk ;
      if (yi < 0 || yi >= height)
        continue ;

      for (zk = -wsize ; zk <= wsize ; zk++)
      {
        zi = z+zk ;
        if (zi < 0 || zi >= depth)
          continue ;

        gcan = &gca->nodes[xi][yi][zi] ;

        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          gc = &gcan->gcs[n] ;
          prior = get_node_prior(gca, gcan->labels[n], xi, yi, zi) ;
          if (gcan->labels[n] != label || prior < min_prior)
            continue ;
          for (dist = 0, r = 0 ; r < gca->ninputs ; r++)
            dist += SQR(gc->means[r] - means[r]) ;
          if (dist < min_dist)
          {
            min_dist = dist ;
            best_gc = gc ;
            best_x = xi ;
            best_y = yi ;
            best_z = zi ;
            best_prior = prior ;
          }
        }
      }
    }
  }

  if (best_x < 0)
  {
    ErrorPrintf(ERROR_BADPARM,
                "could not find GC for label %d at (%d,%d,%d)\n",
                label, x, y, z) ;
    return(ERROR_BADPARM) ;
  }
  if (best_x == 141/4 && best_y == 37*4 && best_z == 129*4)
    DiagBreak() ;
  gcas->xp = best_x ;
  gcas->yp = best_y ;
  gcas->zp = best_z ;
  gcas->label = label ;
  for (v = r = 0 ; r < gca->ninputs ; r++)
  {
    gcas->means[r] = best_gc->means[r] ;
    for (c = r ; c < gca->ninputs ; c++, v++)
      gcas->covars[v] = best_gc->covars[v] ;
  }

  gcas->prior = best_prior ;
  return(NO_ERROR) ;
}
#endif

// create a volume mapped to the current mri volume
int
GCAtransformAndWriteSamples(GCA *gca, MRI *mri, GCA_SAMPLE *gcas,
                            int nsamples,char *fname,TRANSFORM *transform)
{
  int    n, xv, yv, zv, label ;
  MRI    *mri_dst ;

  mri_dst = MRIalloc(mri->width, mri->height, mri->depth, MRI_UCHAR) ;
  MRIcopyHeader(mri, mri_dst);

  TransformInvert(transform, mri) ;
  for (n = 0 ; n < nsamples ; n++)
  {
    if (gcas[n].label == Gdiag_no)
      DiagBreak() ;
    if (!GCApriorToSourceVoxel(gca, mri_dst, transform,
                               gcas[n].xp, gcas[n].yp, gcas[n].zp,
                               &xv, &yv, &zv))
    {
      if (gcas[n].label > 0)
        label = gcas[n].label ;
      else if (gcas[n].label == 0)
        label = 29 ;  /* Left undetermined - visible */
      else
        label = 0 ;  /* Left undetermined - visible */

      mriFillRegion(mri_dst, xv, yv, zv, label, 0) ;

      if (gcas[n].x == Gx && gcas[n].y == Gy && gcas[n].z == Gz)
        DiagBreak() ;

      gcas[n].x = xv ;
      gcas[n].y = yv ;
      gcas[n].z = zv ;

      //////////// diag /////////////////////////
      if (gcas[n].x == Gx && gcas[n].y == Gy && gcas[n].z == Gz)
        DiagBreak() ;
      if (DIAG_VERBOSE_ON && Gdiag & DIAG_SHOW)
        printf("label %d: (%d, %d, %d) <-- (%d, %d, %d)\n",
               gcas[n].label,gcas[n].xp,gcas[n].yp,gcas[n].zp,
               xv, yv, zv) ;
      //////////////////////////////////////////////
    }
  }
  fprintf(stdout, "writing samples to %s...\n", fname) ;
  MRIwrite(mri_dst, fname) ;
  MRIfree(&mri_dst) ;
  fflush(stdout) ;

  return(NO_ERROR) ;
}

static int
mriFillRegion(MRI *mri, int x, int y, int z, int fill_val, int whalf)
{
  int   xi, xk, yi, yk, zi, zk ;

  for (xk = -whalf ; xk <= whalf ; xk++)
  {
    xi = mri->xi[x+xk] ;
    for (yk = -whalf ; yk <= whalf ; yk++)
    {
      yi = mri->yi[y+yk] ;
      for (zk = -whalf ; zk <= whalf ; zk++)
      {
        zi = mri->zi[z+zk] ;
        MRIvox(mri, xi, yi, zi) = fill_val ;
      }
    }
  }
  return(NO_ERROR) ;
}

static int
gcaFindIntensityBounds(GCA *gca, float *pmin, float *pmax)
{
  int      width, depth, height, x, y, z, n, label, r ;
  GCA_NODE *gcan ;
  GC1D     *gc ;
  float    mn[MAX_GCA_INPUTS], mx[MAX_GCA_INPUTS], offset ;

  for (r = 0 ; r < gca->ninputs ; r++)
  {
    mn[r] = 100000.0f ;
    mx[r] = -mn[r] ;
  }
  width = gca->node_width ;
  height = gca->node_height ;
  depth = gca->node_depth ;

  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          gc = &gcan->gcs[n] ;
          label = gcan->labels[n] ;
          if (label == 0)  /* don't include unknowns */
            continue ;
          if (get_node_prior(gca, label, x, y, z) < 0.1)
            continue ;
          /* exclude unlikely stuff (errors in labeling) */
          offset = 0.5*pow(covariance_determinant\
                           (gc, gca->ninputs),
                           1.0/(double)gca->ninputs) ;
          for (r = 0 ; r < gca->ninputs ; r++)
          {
            if (gc->means[r] + offset > mx[r])
            {
              mx[r] = gc->means[r] + offset ;
            }
            if (gc->means[r]  < mn[r])
            {
              mn[r] = gc->means[r] ;
            }
#if 0
            if (mn[r] < 5)
              mn = 5 ;
            if (mx[r] > 225)
              mx[r] = 225 ;
#endif
          }
        }
      }
    }
  }
  for (r = 0 ; r < gca->ninputs ; r++)
  {
    pmin[r] = mn[r] ;
    pmax[r] = mx[r] ;
  }
  return(NO_ERROR) ;
}
static int
gcaFindMaxPriors(GCA *gca, float *max_priors)
{
  int       width, depth, height, x, y, z, n, label ;
  GCA_PRIOR *gcap ;

  memset(max_priors, 0, MAX_DIFFERENT_LABELS*sizeof(float)) ;
  width = gca->prior_width ;
  height = gca->prior_height ;
  depth = gca->prior_depth ;

  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        gcap = &gca->priors[x][y][z] ;
        if (gcap == NULL || gcap->nlabels <= 0)
          continue;
        for (n = 0 ; n < gcap->nlabels ; n++)
        {
          label = gcap->labels[n] ;
          if (gcap->priors[n] > max_priors[label])
          {
            if (label == Ggca_label)
              DiagBreak() ;
            if ((gcap->priors[n] > 0.89) && (label == Ggca_label))
              DiagBreak() ;
            max_priors[label] = gcap->priors[n] ;
          }
        }
      }
    }
  }
  return(NO_ERROR) ;
}

#if 0
static int xn_bad = 15 ;
static int yn_bad = 24 ;
static int zn_bad = 27 ;
static int xl_bad = 56 ;
static int yl_bad = 99 ;
static int zl_bad = 106 ;
static int label_bad = 42 ;
static int nbr_label_bad = 42 ;
static int bad_i = 0 ;
#endif

//                                 segmented volume here
static int
GCAupdateNodeGibbsPriors(GCA *gca, MRI*mri, int xn, int yn, int zn,
                         int xl, int yl, int zl, int label)
{
  int       n, i, xnbr, ynbr, znbr, nbr_label ;
  GCA_NODE *gcan ;
  GC1D     *gc ;

  gcan = &gca->nodes[xn][yn][zn] ;

  // look for this label
  for (n = 0 ; n < gcan->nlabels ; n++)
  {
    if (gcan->labels[n] == label)
      break ;
  }
  // not found
  if (n >= gcan->nlabels)  /* have to allocate a new classifier */
    ErrorExit(ERROR_BADPARM, "gca(%d, %d, %d): could not find label %d",
              xn, yn, zn, label) ;

  gc = &gcan->gcs[n] ;
  for (i = 0 ; i < GIBBS_NEIGHBORHOOD ; i++)
  {
    /* coordinates of neighboring point */
    xnbr = mri->xi[xl+xnbr_offset[i]] ;// xnbr_offset 1, -1, 0,  0, 0,  0
    ynbr = mri->yi[yl+ynbr_offset[i]] ;// ynbr_offset 0,  0, 1, -1, 0,  0
    znbr = mri->zi[zl+znbr_offset[i]] ;// znbr_offset 0,  0, 0,  0, 1, -1

    // get the label from the neighbor
    nbr_label = nint(MRIgetVoxVal(mri, xnbr, ynbr, znbr, 0)) ;
    if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z && label == Ggca_label
        && (xnbr_offset[i] == 1) && (nbr_label == Ggca_nbr_label))
    {
      printf("(%d, %d, %d) --> node (%d, %d, %d), "
             "label %s (%d), nbr (%d, %d, %d) = %s (%d)\n",
             xl, yl, zl, xn, yn, zn, cma_label_to_name(label), label,
             xnbr, ynbr, znbr, cma_label_to_name(nbr_label), nbr_label) ;

    }

    /* now see if this label exists already as a nbr */
    for (n = 0 ; n < gc->nlabels[i] ; n++)
      if (gc->labels[i][n] == nbr_label)
        break ;

    if (n >= gc->nlabels[i])   /* not there - reallocate stuff */
    {
#if 1
      unsigned short *old_labels ;
      float *old_label_priors ;

      old_labels = gc->labels[i] ;
      old_label_priors = gc->label_priors[i] ;

      /* allocate new ones */
      gc->label_priors[i] =
        (float *)calloc(gc->nlabels[i]+1, sizeof(float)) ;
      if (!gc->label_priors[i])
        ErrorExit(ERROR_NOMEMORY, "GCAupdateNodeGibbsPriors: "
                  "couldn't expand gcs to %d" ,gc->nlabels[i]+1) ;
      gc->labels[i] =
        (unsigned short *)calloc(gc->nlabels[i]+1, sizeof(unsigned short)) ;
      if (!gc->labels[i])
        ErrorExit(ERROR_NOMEMORY,
                  "GCANupdateNode: couldn't expand labels to %d",
                  gc->nlabels[i]+1) ;

      if (gc->nlabels[i] > 0)  /* copy the old ones over */
      {
        memmove(gc->label_priors[i], old_label_priors,
                gc->nlabels[i]*sizeof(float)) ;
        memmove(gc->labels[i], old_labels,
                gc->nlabels[i]*sizeof(unsigned short)) ;

        /* free the old ones */
        free(old_label_priors) ;
        free(old_labels) ;
      }
      gc->labels[i][gc->nlabels[i]++] = nbr_label ;
#else
      if (n >= MAX_NBR_LABELS)   /* not there - reallocate stuff */
      {
        ErrorPrintf(ERROR_NOMEMORY,
                    "GCAupdateNodeGibbsPriors: "
                    "gca(%d,%d,%d) has more than %d "
                    "different neighbors", xn, yn, zn, MAX_NBR_LABELS);
        continue ;
      }
      else
        gc->labels[i][gc->nlabels[i]++] = nbr_label ;
#endif
    }
    gc->label_priors[i][n] += 1.0f ; // counter increment at this label
  }

  return(NO_ERROR) ;
}

#if 0
MRI  *
GCAreclassifyUsingGibbsPriors(MRI *mri_inputs,
                              GCA *gca,
                              MRI *mri_dst,
                              TRANSFORM *transform,
                              int max_iter)
{
  int      x, y, z, width, height, depth, label, val, iter,
  xn, yn, zn, n, i, j, nchanged, xnbr, ynbr, znbr, nbr_label,
  index, nindices ;
  short    *x_indices, *y_indices, *z_indices ;
  GCA_NODE *gcan ;
  GC1D     *gc ;
  double   dist, max_p, p, prior, ll, lcma = 0.0, new_ll, old_ll ;
  MRI      *mri_changed, *mri_probs ;

  nindices = mri_dst->width * mri_dst->height * mri_dst->depth ;
  x_indices = (short *)calloc(nindices, sizeof(short)) ;
  y_indices = (short *)calloc(nindices, sizeof(short)) ;
  z_indices = (short *)calloc(nindices, sizeof(short)) ;
  if (!x_indices || !y_indices || !z_indices)
    ErrorExit(ERROR_NOMEMORY, "GCAreclassifyUsingGibbsPriors: "
              "could not allocate index set") ;


  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  iter = 0 ;
  if (!mri_dst)
  {
    mri_dst = MRIalloc(width, height, depth, MRI_UCHAR) ;
    if (!mri_dst)
      ErrorExit(ERROR_NOMEMORY, "GCAlabel: could not allocate dst") ;
    MRIcopyHeader(mri_inputs, mri_dst) ;
  }

  mri_changed = MRIclone(mri_dst, NULL) ;


  /* go through each voxel in the input volume and find the canonical
     voxel (and hence the classifier) to which it maps. Then update the
     classifiers statistics based on this voxel's intensity and label.
  */
  for (x = 0 ; x < width ; x++)
    for (y = 0 ; y < height ; y++)
      for (z = 0 ; z < depth ; z++)
        MRIvox(mri_changed,x,y,z) = 1 ;

#if 0
  {
    MRI   *mri_cma ;
    char  fname[STRLEN], *cp ;

    strcpy(fname, mri_inputs->fname) ;
    cp = strrchr(fname, '/') ;
    if (cp)
    {
      strcpy(cp+1, "parc") ;
      mri_cma = MRIread(fname) ;
      if (mri_cma)
      {

        ll = gcaGibbsImageLogLikelihood(gca, mri_dst, mri_inputs, lta) ;
        lcma = gcaGibbsImageLogLikelihood(gca, mri_cma, mri_inputs, lta) ;
        lcma /= (double)(width*depth*height) ;
        ll /= (double)(width*depth*height) ;
        fprintf(stdout, "image ll: %2.3f (CMA=%2.3f)\n", ll, lcma) ;
        MRIfree(&mri_cma) ;
      }
    }
  }
#endif
  fflush(stdout) ;

  do
  {
    if (iter == 0)
    {
      mri_probs = GCAlabelProbabilities(mri_inputs, gca, NULL, lta) ;
      MRIorderIndices(mri_probs, x_indices, y_indices, z_indices) ;
      MRIfree(&mri_probs) ;
    }
    else
      MRIcomputeVoxelPermutation(mri_inputs, x_indices, y_indices,
                                 z_indices) ;

    nchanged = 0 ;
    for (index = 0 ; index < nindices ; index++)
    {
      x = x_indices[index] ;
      y = y_indices[index] ;
      z = z_indices[index] ;
      if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                transform, x, y, z, &xn, &yn, &zn))
      {
        if (x == 100 && y == 104 && z == 130)
          DiagBreak() ;

#if 1
        if (MRIvox(mri_changed, x, y, z) == 0)
          continue ;
#endif

        if (x == 63 && y == 107 && z == 120)
          DiagBreak() ;

        val = MRIgetVoxVal(mri_inputs, x, y, z, 0) ;

        /* find the node associated with this coordinate and classify */
        // this is checked above
        GCAsourceVoxelToNode(gca, mri_inputs,
                             transform, x, y, z, &xn, &yn, &zn) ;
        gcan = &gca->nodes[xn][yn][zn] ;
        label = 0 ;
        max_p = 2*GIBBS_NEIGHBORS*BIG_AND_NEGATIVE ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          gc = &gcan->gcs[n] ;

          /* compute 1-d Mahalanobis distance */
          dist = (val-gc->mean) ;
#define USE_LOG_PROBS  1
          p =
            -log(sqrt(gc->var))
            - .5*(dist*dist/gc->var)
            + log(gc->prior);

          for (prior = 0.0f, i = 0 ; i < GIBBS_NEIGHBORS ; i++)
          {
            xnbr = mri_dst->xi[x+xnbr_offset[i]] ;
            ynbr = mri_dst->yi[y+ynbr_offset[i]] ;
            znbr = mri_dst->zi[z+znbr_offset[i]] ;
            nbr_label = nint(MRIgetVoxVal(mri_dst, xnbr, ynbr, znbr,0)) ;
            for (j = 0 ; j < gc->nlabels[i] ; j++)
            {
              if (nbr_label == gc->labels[i][j])
                break ;
            }
            if (j < gc->nlabels[i])  /* found this label */
            {
              if (!FZERO(gc->label_priors[i][j]))
                prior += log(gc->label_priors[i][j]) ;
              else
                prior += log(0.1f/(float)(gcan->total_training)) ;
              /*BIG_AND_NEGATIVE*/
            }
            else
            {
              if (x == 141 && y == 62 && z == 126)
                DiagBreak() ;
              prior += log(0.1f/(float)(gcan->total_training));
              /*2*GIBBS_NEIGHBORS*BIG_AND_NEGATIVE */
              /* never occurred - make it unlikely */
            }
            p += prior ;
            if (p > max_p)
            {
              max_p = p ;
              label = gcan->labels[n] ;
            }
          }

          if (FZERO(max_p))
          {
            label = 0 ;
            max_p = 2*GIBBS_NEIGHBORS*BIG_AND_NEGATIVE ;
            for (n = 0 ; n < gcan->nlabels ; n++)
            {
              gc = &gcan->gcs[n] ;

              /* compute 1-d Mahalanobis distance */
              dist = (val-gc->mean) ;
              if (FZERO(gc->var))  /* make it a delta function */
              {
                if (FZERO(dist))
                  p = 1.0 ;
                else
                  p = 0.0 ;
              }
              else
                p = 1 / sqrt(gc->var * 2 * M_PI)
                    * exp(-dist*dist/gc->var) ;

              p *= gc->prior ;
              if (p > max_p)
              {
                max_p = p ;
                label = gcan->labels[n] ;
              }
            }
          }
        }
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
        {
          printf(
            "(%d, %d, %d): old label %s (%d), "
            "new label %s (%d) (p=%2.3f)\n",
            x, y, z, cma_label_to_name(nint(MRIgetVoxVal(mri_dst,x,y,z,0))),
            nint(MRIgetVoxVal(mri_dst,x,y,z,0)),
            cma_label_to_name(label), label, max_p) ;
          if (label == Ggca_label)
          {
            DiagBreak() ;
          }
        }

        if (nint(MRIgetVoxVal(mri_dst, x, y, z,0)) != label)
        {
          int old_label = nint(MRIgetVoxVal(mri_dst, x, y, z, 0)) ;
          if (x == 100 && y == 104 && z == 130)
            DiagBreak() ;
          old_ll =
            gcaNbhdGibbsLogLikelihood(gca,
                                      mri_dst,
                                      mri_inputs,
                                      x, y, z, transform,
                                      PRIOR_FACTOR) ;
          MRIsetVoxVal(mri_dst, x, y, z, 0, label) ;
          new_ll =
            gcaNbhdGibbsLogLikelihood(gca,
                                      mri_dst,
                                      mri_inputs,
                                      x, y, z, transform,
                                      PRIOR_FACTOR) ;
          if (new_ll > old_ll)
          {
            MRIvox(mri_changed, x, y, z) = 1 ;
            nchanged++ ;
          }
          else
          {
            MRIsetVoxVal(mri_dst, x, y, z, 0, old_label) ;
            MRIvox(mri_changed, x, y, z) = 0 ;
          }
        }
        else
          MRIvox(mri_changed, x, y, z) = 0 ;
      } // if (!GCAsource...)
    } // index loop
    ll = gcaGibbsImageLogLikelihood(gca, mri_dst, mri_inputs, lta) ;
    ll /= (double)(width*depth*height) ;
    if (!FZERO(lcma))
      printf("pass %d: %d changed. image ll: %2.3f (CMA=%2.3f)\n",
             iter+1, nchanged, ll, lcma) ;
    else
      printf("pass %d: %d changed. image ll: %2.3f\n",
             iter+1, nchanged, ll) ;
    MRIdilate(mri_changed, mri_changed) ;

  }
  while (nchanged > 0 && iter++ < max_iter) ;

  free(x_indices) ;
  free(y_indices) ;
  free(z_indices) ;
  MRIfree(&mri_changed) ;


  return(mri_dst) ;
}
#endif

MRI  *
GCAanneal(MRI *mri_inputs, GCA *gca, MRI *mri_dst,TRANSFORM *transform,
          int max_iter)
{
  int      x, y, z, width, height, depth, label, val, iter,
  xn, yn, zn, n, nchanged,
  index, nindices, old_label ;
  short    *x_indices, *y_indices, *z_indices ;
  GCA_NODE *gcan ;
  double   ll, lcma = 0.0, old_ll, new_ll, min_ll ;
  MRI      *mri_changed, *mri_probs ;

  printf("performing simulated annealing...\n") ;

  nindices = mri_dst->width * mri_dst->height * mri_dst->depth ;
  x_indices = (short *)calloc(nindices, sizeof(short)) ;
  y_indices = (short *)calloc(nindices, sizeof(short)) ;
  z_indices = (short *)calloc(nindices, sizeof(short)) ;
  if (!x_indices || !y_indices || !z_indices)
    ErrorExit(ERROR_NOMEMORY, "GCAanneal: "
              "could not allocate index set") ;


  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  iter = 0 ;
  if (!mri_dst)
  {
    mri_dst = MRIalloc(width, height, depth, MRI_UCHAR) ;
    if (!mri_dst)
      ErrorExit(ERROR_NOMEMORY, "GCAlabel: could not allocate dst") ;
    MRIcopyHeader(mri_inputs, mri_dst) ;
  }

  mri_changed = MRIclone(mri_dst, NULL) ;

  /* go through each voxel in the input volume and find the canonical
     voxel (and hence the classifier) to which it maps. Then update the
     classifiers statistics based on this voxel's intensity and label.
  */
  for (x = 0 ; x < width ; x++)
    for (y = 0 ; y < height ; y++)
      for (z = 0 ; z < depth ; z++)
        MRIvox(mri_changed,x,y,z) = 1 ;

  old_ll = gcaGibbsImageLogLikelihood(gca, mri_dst, mri_inputs, transform) ;
  old_ll /= (double)(width*depth*height) ;
#if 0
  {
    MRI   *mri_cma ;
    char  fname[STRLEN], *cp ;

    strcpy(fname, mri_inputs->fname) ;
    cp = strrchr(fname, '/') ;
    if (cp)
    {
      strcpy(cp+1, "parc") ;
      mri_cma = MRIread(fname) ;
      if (mri_cma)
      {

        lcma = gcaGibbsImageLogLikelihood(gca, mri_cma,
                                          mri_inputs, transform) ;
        lcma /= (double)(width*depth*height) ;
        fprintf(stdout, "image ll: %2.3f (CMA=%2.3f)\n", old_ll, lcma) ;
        MRIfree(&mri_cma) ;
      }
    }
  }
#endif
  fflush(stdout) ;

  do
  {
    if (iter == 0)
    {
      mri_probs = GCAlabelProbabilities(mri_inputs,
                                        gca, mri_dst, transform) ;
      MRIorderIndices(mri_probs, x_indices, y_indices, z_indices) ;
      MRIfree(&mri_probs) ;
    }
    else
      MRIcomputeVoxelPermutation(mri_inputs, x_indices, y_indices,
                                 z_indices) ;

    nchanged = 0 ;
    for (index = 0 ; index < nindices ; index++)
    {
      x = x_indices[index] ;
      y = y_indices[index] ;
      z = z_indices[index] ;
      if (x == 155 && y == 126 && z == 128)
        DiagBreak() ;

#if 1
      if (MRIvox(mri_changed, x, y, z) == 0)
        continue ;
#endif

      if (x == 63 && y == 107 && z == 120)
        DiagBreak() ;

      val = MRIgetVoxVal(mri_inputs, x, y, z, 0) ;

      /* find the node associated with this coordinate and classify */
      if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                transform, x, y, z, &xn, &yn, &zn))
      {
        gcan = &gca->nodes[xn][yn][zn] ;


        label = old_label = nint(MRIgetVoxVal(mri_dst, x, y, z,0)) ;
        min_ll = gcaNbhdGibbsLogLikelihood(gca,
                                           mri_dst,
                                           mri_inputs,
                                           x, y,z,transform,
                                           PRIOR_FACTOR);

        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (gcan->labels[n] == old_label)
            continue ;
          MRIsetVoxVal(mri_dst, x, y, z, 0, gcan->labels[n]) ;
          new_ll =
            gcaNbhdGibbsLogLikelihood(gca,
                                      mri_dst, mri_inputs,
                                      x, y,z,transform,
                                      PRIOR_FACTOR);
          if (new_ll > min_ll)
          {
            min_ll = new_ll ;
            label = gcan->labels[n] ;
          }
        }
        if (label != old_label)
        {
          nchanged++ ;
          MRIvox(mri_changed, x, y, z) = 1 ;
        }
        else
          MRIvox(mri_changed, x, y, z) = 0 ;
        MRIsetVoxVal(mri_dst, x, y, z, 0,label) ;
      }
    } // index loop
    if (nchanged > 10000)
    {
      ll = gcaGibbsImageLogLikelihood(gca, mri_dst, mri_inputs,
                                      transform) ;
      ll /= (double)(width*depth*height) ;
      if (!FZERO(lcma))
        printf("pass %d: %d changed. image ll: %2.3f (CMA=%2.3f)\n",
               iter+1, nchanged, ll, lcma) ;
      else
        printf("pass %d: %d changed. image ll: %2.3f\n",
               iter+1, nchanged, ll);
    }
    else
      printf("pass %d: %d changed.\n", iter+1, nchanged) ;
    MRIdilate(mri_changed, mri_changed) ;
  }
  while (nchanged > 0 && iter++ < max_iter) ;

  free(x_indices) ;
  free(y_indices) ;
  free(z_indices) ;
  MRIfree(&mri_changed) ;

  return(mri_dst) ;
}

char *gca_write_fname = NULL ;
int gca_write_iterations = 0 ;

#define MAX_PRIOR_FACTOR 1.0
#define MIN_PRIOR_FACTOR 1

MRI  *
GCAreclassifyUsingGibbsPriors(MRI *mri_inputs,
                              GCA *gca,
                              MRI *mri_dst,
                              TRANSFORM *transform,
                              int max_iter, MRI *mri_fixed, int restart,
                              void (*update_func)(MRI *))
{
  int      x, y, z, width, height, depth, label, val, iter,
  n, nchanged, min_changed, index, nindices, old_label, fixed , max_label ;
short    *x_indices, *y_indices, *z_indices ;
GCA_PRIOR *gcap ;
double   ll, lcma = 0.0, old_ll, new_ll, max_ll ;
MRI      *mri_changed, *mri_probs /*, *mri_zero */ ;

// fixed is the label fixed volume, e.g. wm
fixed = (mri_fixed != NULL) ;

#if 0
  GCAannealUnlikelyVoxels(mri_inputs, gca, mri_dst, transform, max_iter*100,
                          mri_fixed) ;
  {
    char  fname[STRLEN], *cp ;

    strcpy(fname, mri_inputs->fname) ;
    cp = strrchr(fname, '/') ;
    if (cp)
    {
      strcpy(cp+1, "anneal") ;
      fprintf(stdout, "writing results of annealing to %s...\n", fname) ;
      MRIwrite(mri_dst, fname) ;
    }
  }
#endif

  nindices = mri_dst->width * mri_dst->height * mri_dst->depth ;
  x_indices = (short *)calloc(nindices, sizeof(short)) ;
  y_indices = (short *)calloc(nindices, sizeof(short)) ;
  z_indices = (short *)calloc(nindices, sizeof(short)) ;
  if (!x_indices || !y_indices || !z_indices)
    ErrorExit(ERROR_NOMEMORY, "GCAreclassifyUsingGibbsPriors: "
              "could not allocate index set") ;


#if 0
  mri_zero = MRIclone(mri_inputs, NULL) ;
#endif
  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  iter = 0 ;
  if (!mri_dst)
  {
    mri_dst = MRIalloc(width, height, depth, MRI_UCHAR) ;
    if (!mri_dst)
      ErrorExit(ERROR_NOMEMORY, "GCAlabel: could not allocate dst") ;
    MRIcopyHeader(mri_inputs, mri_dst) ;
  }

  mri_changed = MRIclone(mri_dst, NULL) ;

  /* go through each voxel in the input volume and find the canonical
     voxel (and hence the classifier) to which it maps. Then update the
     classifiers statistics based on this voxel's intensity and label.
  */
  // mark changed location
  for (x = 0 ; x < width ; x++)
    for (y = 0 ; y < height ; y++)
      for (z = 0 ; z < depth ; z++)
      {
        if (restart && mri_fixed)
        {
          // mark only fixed voxels
          if (MRIvox(mri_fixed, x, y, z))
            MRIvox(mri_changed,x,y,z) = 1 ;
        }
        else
          // everything is marked
          MRIvox(mri_changed,x,y,z) = 1 ;
      }

  if (restart && mri_fixed)
    MRIdilate(mri_changed, mri_changed) ;

  if (!restart)
  {
    // calculate the statistics
    old_ll = gcaGibbsImageLogLikelihood(gca, mri_dst,
                                        mri_inputs, transform) ;
    // get the per voxel value
    old_ll /= (double)(width*depth*height) ;
  }
  else
    old_ll = 0 ;

#if 0
  if (0)
  {
    MRI   *mri_cma ;
    char  fname[STRLEN], *cp ;

    strcpy(fname, mri_inputs->fname) ;
    cp = strrchr(fname, '/') ;
    if (cp)
    {
      strcpy(cp+1, "parc") ;
      mri_cma = MRIread(fname) ;
      if (mri_cma)
      {
        lcma = gcaGibbsImageLogLikelihood(gca, mri_cma,
                                          mri_inputs, transform) ;
        lcma /= (double)(width*depth*height) ;
        fprintf(stdout, "image ll: %2.3f (CMA=%2.3f)\n", old_ll, lcma) ;
        MRIfree(&mri_cma) ;
      }
    }
  }
#endif

  PRIOR_FACTOR = MIN_PRIOR_FACTOR ;
  do
  {
    if (restart)
    {
      for (index = x = 0 ; x < width ; x++)
        for (y = 0 ; y < height ; y++)
          for (z = 0 ; z < depth ; z++)
          {
            // not fixed voxel, but changed
            if (MRIvox(mri_fixed, x, y, z) == 0 &&
                (MRIvox(mri_changed,x,y,z) > 0))
            {
              x_indices[index] = x ;
              y_indices[index] = y ;
              z_indices[index] = z ;
              index++ ;
            }
          }
      nindices = index ;
    }
    else if (iter == 0)
    {
      /*      char  fname[STRLEN], *cp ;*/
      /*      int   nfixed ;*/

      if (gca_write_iterations)
      {
        char fname[STRLEN] ;
        sprintf(fname, "%s%03d.mgz", gca_write_fname, iter) ;
        printf("writing snapshot to %s...\n", fname) ;
        MRIwrite(mri_dst, fname) ;
      }
      // probs has 0 to 255 values
      mri_probs = GCAlabelProbabilities(mri_inputs, gca, NULL, transform) ;
      // sorted according to ascending order of probs
      MRIorderIndices(mri_probs, x_indices, y_indices, z_indices) ;
      MRIfree(&mri_probs) ;
    }
    else
      // randomize the indices value ((0 -> width*height*depth)
      MRIcomputeVoxelPermutation(mri_inputs, x_indices, y_indices,
                                 z_indices) ;

    ///////////////  relabel with neighborhood likelihood  //////////////
    nchanged = 0 ;
    if (G_write_probs && !mri_probs)
    {
      mri_probs = MRIalloc(mri_inputs->width, mri_inputs->height,
                           mri_inputs->depth, MRI_FLOAT) ;
      MRIcopyHeader(mri_inputs, mri_probs) ;
    }
    for (index = 0 ; index < nindices ; index++)
    {
      x = x_indices[index] ;
      y = y_indices[index] ;
      z = z_indices[index] ;
      if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
        DiagBreak() ;

      // if the label is fixed, don't do anything
      if (mri_fixed && MRIvox(mri_fixed, x, y, z))
        continue ;

      // if not marked, don't do anything
      if (MRIvox(mri_changed, x, y, z) == 0)
        continue ;

      // get the grey value
      val = MRIgetVoxVal(mri_inputs, x, y, z, 0) ;

      /* find the node associated with this coordinate and classify */
      gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
      // it is not in the right place
      if (gcap==NULL)
        continue;
      // only one label associated, don't do anything
      if (gcap->nlabels == 1)
        continue ;

      // save the current label
      label = old_label = nint(MRIgetVoxVal(mri_dst, x, y, z,0)) ;
      // calculate neighborhood likelihood
      max_ll = gcaNbhdGibbsLogLikelihood(gca, mri_dst,
                                         mri_inputs, x, y,z,transform,
                                         PRIOR_FACTOR);

      // go through all labels at this point
      for (n = 0 ; n < gcap->nlabels ; n++)
      {
        // skip the current label
        if (gcap->labels[n] == old_label)
          continue ;
        // assign the new label
        MRIsetVoxVal(mri_dst, x, y, z, 0,gcap->labels[n]) ;
        // calculate neighborhood likelihood
        new_ll =
          gcaNbhdGibbsLogLikelihood(gca, mri_dst,
                                    mri_inputs, x, y,z,transform,
                                    PRIOR_FACTOR);
        // if it is bigger than the old one, then replace the label
        // and change max_ll
        if (new_ll > max_ll)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (label == Ggca_label || old_label ==
               Ggca_label || Ggca_label < 0))
            fprintf(stdout,
                    "NbhdGibbsLogLikelihood at (%d, %d, %d):"
                    " old = %d (ll=%.2f) new = %d (ll=%.2f)\n",
                    x, y, z, old_label, max_ll,
                    gcap->labels[n], new_ll);

          max_ll = new_ll ;
          label = gcap->labels[n] ;
        }
      }

      /*#ifndef __OPTIMIZE__*/
      if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
          (label == Ggca_label || old_label ==
           Ggca_label || Ggca_label < 0))
      {
        int       xn, yn, zn ;
        GCA_NODE *gcan ;

        if (!GCAsourceVoxelToNode(gca, mri_inputs, transform,
                                  x, y, z, &xn, &yn, &zn))
        {
          gcan = &gca->nodes[xn][yn][zn] ;
          printf("(%d, %d, %d): old label %s (%d), "
                 "new label %s (%d) (log(p)=%2.3f)\n",
                 x, y, z, cma_label_to_name(old_label), old_label,
                 cma_label_to_name(label), label, max_ll) ;
          dump_gcan(gca, gcan, stdout, 0, gcap) ;
          if (label == Right_Caudate)
            DiagBreak() ;
        }
      }
      /*#endif*/

      // if label changed
      if (label != old_label)
      {
        nchanged++ ;
        // mark it as changed
        MRIvox(mri_changed, x, y, z) = 1 ;
      }
      else
        MRIvox(mri_changed, x, y, z) = 0 ;
      // assign new label
      MRIsetVoxVal(mri_dst, x, y, z, 0, label) ;
      if (mri_probs)
        MRIsetVoxVal(mri_probs, x, y, z, 0, -max_ll) ;
    }
    if (mri_probs)
    {
      char fname[STRLEN] ;

      sprintf(fname, "%s%03d.mgz", G_write_probs, iter) ;
      printf("writing probabilities to %s...\n", fname) ;
      MRIwrite(mri_probs, fname) ;
      MRIfree(&mri_probs) ;
    }

    if (0)
      // reclassify connected compontents of unlikely labels as a whole
    {
      MRI_SEGMENTATION *mriseg ;
      MRI              *mri_impossible, *mri_impossible_label ;
      int              label, s, nchanged, iter ;
      char             fname[STRLEN] ;
      double           old_ll, new_ll ;

      max_label = GCAmaxLabel(gca) ;
      old_ll = gcaGibbsImageLogLikelihood(gca, mri_dst,
                                          mri_inputs,
                                          transform)/(double)
               (width*depth*height) ;
      iter = 0 ;
      printf("%02d: ll %2.5f\n", iter, old_ll) ;

      do
      {
        nchanged = 0 ;
        mri_impossible =
          GCAmarkImpossible(gca, mri_dst, NULL, transform) ;
        if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
          MRIwrite(mri_impossible, "imp.mgz") ;
        mri_impossible_label = MRIclone(mri_impossible, NULL) ;
        if (gca_write_iterations > 0)
        {
          char fname[STRLEN] ;
          static int fno = 0 ;

          sprintf(fname, "%s_before%03d.mgz",
                  gca_write_fname, fno+1) ;
          fno++ ;
          printf("writing snapshot to %s...\n", fname) ;
          MRIwrite(mri_dst, fname) ;
        }
        for (label = 1 ; label <= max_label ; label++)
        {
          MRIclear(mri_impossible_label) ;

          // find voxels that have label and aren't possible
          if (MRIcopyLabeledVoxels(mri_impossible,
                                   mri_dst,
                                   mri_impossible_label,
                                   label) == 0)
            continue ;  // no voxels in label

          sprintf(fname, "mimp_label%d.mgz", label) ;
          if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
            MRIwrite(mri_impossible_label, fname) ;
          mriseg = MRIsegment(mri_impossible_label, 0.5, 256) ;

          if (Gdiag & DIAG_SHOW && DIAG_VERBOSE_ON)
            printf("%d low probability segments "
                   "found for label %s (%d)...\n",
                   mriseg->nsegments, cma_label_to_name(label),
                   label) ;

          for (s = 0 ; s < mriseg->nsegments ; s++)
          {
            if (gcaRelabelSegment(gca, transform, mri_inputs,
                                  mri_dst, &mriseg->segments[s]) > 0)
            {
              MRIsetSegmentValue(mri_changed, mriseg, s, 1) ;
              nchanged++ ;
            }
          }
          MRIsegmentFree(&mriseg) ;
        }
        MRIfree(&mri_impossible_label) ;
        MRIfree(&mri_impossible) ;
        new_ll = gcaGibbsImageLogLikelihood(gca,
                                            mri_dst, mri_inputs,
                                            transform)/
                 (double)(width*depth*height);
        printf("%02d: ll %2.5f (%d segments changed)\n",
               iter+1, new_ll, nchanged) ;
      }
      while ((nchanged > 0) && (iter++ < 10));
    }

    /////////////////////////////////////////////////////////////////////////
    // print info
    if (nchanged > 10000 && iter < 2 && !restart)
    {
      ll = gcaGibbsImageLogLikelihood(gca, mri_dst,
                                      mri_inputs, transform) ;
      // get the average value
      ll /= (double)(width*depth*height) ;
      if (!FZERO(lcma))
        printf("pass %d: %d changed. image ll: %2.3f "
               "(CMA=%2.3f), PF=%2.3f\n",
               iter+1, nchanged, ll, lcma, PRIOR_FACTOR) ;
      else // currently this is executed
        printf("pass %d: %d changed. image ll: %2.3f, PF=%2.3f\n",
               iter+1, nchanged, ll, PRIOR_FACTOR) ;
    }
    else
      printf("pass %d: %d changed.\n", iter+1, nchanged) ;

    // get the largest 6 neighbor values,
    // that is, originally 0 could become 1
    MRIdilate(mri_changed, mri_changed) ;
    // if unpdate_func is present, use it
    if (update_func)
      (*update_func)(mri_dst) ;

#if 0
    if (!iter && DIAG_VERBOSE_ON)
    {
      char  fname[STRLEN], *cp ;
      /*      int   nvox ;*/

      strcpy(fname, mri_inputs->fname) ;
      cp = strrchr(fname, '/') ;
      if (cp)
      {
        strcpy(cp+1, "zero") ;
        nvox = MRIvoxelsInLabel(mri_zero, 255) ;
        fprintf(stdout, "writing %d low probability points to %s...\n",
                nvox, fname) ;
        MRIwrite(mri_zero, fname) ;
        MRIfree(&mri_zero) ;
      }
    }
#endif
#define MIN_CHANGED 5000
    min_changed = restart ? 0 : MIN_CHANGED ;
    if (nchanged <= min_changed ||
        (restart && iter >= max_iter))
    {
      if (restart)
        iter = 0 ;
#if 0
      if (restart)
        break ;
#endif

      if (!restart)  /* examine whole volume next time */
      {
        for (x = 0 ; x < width ; x++)
          for (y = 0 ; y < height ; y++)
            for (z = 0 ; z < depth ; z++)
              MRIvox(mri_changed,x,y,z) = 1 ;
      }
      if (fixed && !restart)
      {
        printf("removing fixed flag...\n") ;
        if (mri_fixed)
          MRIclear(mri_fixed) ;
        fixed = 0 ;
    }
    else
    {
      PRIOR_FACTOR *= 2 ;
      if (PRIOR_FACTOR < MAX_PRIOR_FACTOR)
          fprintf(stdout, "setting PRIOR_FACTOR to %2.4f\n",
                  PRIOR_FACTOR) ;
      }
      if (gca_write_iterations > 0)
      {
        char fname[STRLEN] ;

        sprintf(fname, "%s_iter%d.mgz", gca_write_fname, iter+1) ;
        printf("writing snapshot to %s...\n", fname) ;
        MRIwrite(mri_dst, fname) ;
      }
    }
    if ((gca_write_iterations > 0) && !(iter % gca_write_iterations))
    {
      char fname[STRLEN] ;
      sprintf(fname, "%s%03d.mgz", gca_write_fname, iter+1) ;
      printf("writing snapshot to %s...\n", fname) ;
      MRIwrite(mri_dst, fname) ;
    }
  }
  while ((nchanged > MIN_CHANGED || PRIOR_FACTOR < MAX_PRIOR_FACTOR) &&
         (iter++ < max_iter)) ;

#if 0
  {
    char  fname[STRLEN], *cp ;
    int   nzero, n_nonzero ;

    strcpy(fname, mri_inputs->fname) ;
    cp = strrchr(fname, '/') ;
    if (cp)
    {
      strcpy(cp+1, "indices") ;
      mri_probs = GCAcomputeProbabilities(mri_inputs, gca,
                                          mri_dst,NULL, transform) ;
      MRIorderIndices(mri_probs, x_indices, y_indices, z_indices) ;
      for (nzero = index = 0 ; index < nindices ; index++, nzero++)
      {
        x = x_indices[index] ;
        y = y_indices[index] ;
        z = z_indices[index] ;
        if (MRIvox(mri_probs, x, y, z) != 255)
          break ;
        MRIvox(mri_probs, x, y, z) = 0 ;
      }
      n_nonzero = nindices - nzero ;
      for ( ; index < nindices ; index++)
      {
        x = x_indices[index] ;
        y = y_indices[index] ;
        z = z_indices[index] ;
        if (MRIvox(mri_probs, x, y, z) == 255)
          MRIvox(mri_probs, x, y, z) = 0 ;
        else
        {
          MRIvox(mri_probs, x, y, z) =
            100 * (float)(n_nonzero-index)/n_nonzero ;
        }
      }
      MRIwrite(mri_probs, fname) ;
    }
  }
#endif



  if (mri_probs)
    MRIfree(&mri_probs) ;

  free(x_indices) ;
  free(y_indices) ;
  free(z_indices) ;
  MRIfree(&mri_changed) ;

  return(mri_dst) ;
}
int
MRIcomputeVoxelPermutation(MRI *mri, short *x_indices, short *y_indices,
                           short *z_indices)
{
  int width, height, depth, tmp, nindices, i, index ;

  width = mri->width, height = mri->height ;
  depth = mri->depth ;
  nindices = width*height*depth ;

  for (i = 0 ; i < nindices ; i++)
  {
    x_indices[i] = i % width ;
    y_indices[i] = (i/width) % height ;
    z_indices[i] = (i / (width*height)) % depth ;
  }
  for (i = 0 ; i < nindices ; i++)
  {
    index = (int)randomNumber(0.0, (double)(nindices-0.0001)) ;

    tmp = x_indices[index] ;
    x_indices[index] = x_indices[i] ;
    x_indices[i] = tmp ;

    tmp = y_indices[index] ;
    y_indices[index] = y_indices[i] ;
    y_indices[i] = tmp ;

    tmp = z_indices[index] ;
    z_indices[index] = z_indices[i] ;
    z_indices[i] = tmp ;
  }
  return(NO_ERROR) ;
}

#if 0
static int
gcaGibbsSort(GCA *gca, MRI *mri_labels, MRI *mri_inputs,
             TRANSFORM *transform)
{
  int    x, y, z, width, depth, height ;
  double total_log_likelihood, log_likelihood ;
  MRI    *mri_probs ;

  width = mri_labels->width ;
  height = mri_labels->height ;
  depth = mri_labels->depth ;
  mri_probs = MRIclone(mri_labels, NULL) ;

  for (total_log_likelihood = 0.0, x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        log_likelihood =
          gcaVoxelGibbsLogLikelihood(gca, mri_labels,
                                     mri_inputs, x, y, z,transform,
                                     PRIOR_FACTOR);
        log_likelihood *= 20 ;
        if (log_likelihood > 255)
          log_likelihood = 255 ;
        MRIvox(mri_probs,x,y,z) = log_likelihood ;
        total_log_likelihood += log_likelihood ;
      }
    }
  }
  MRIorderIndices(mri_probs, x_indices, y_indices, z_indices) ;
  MRIfree(&mri_probs) ;
  return(NO_ERROR) ;
}
#endif

static double
gcaGibbsImageLogLikelihood(GCA *gca, MRI *mri_labels, MRI *mri_inputs,
                           TRANSFORM *transform)
{
  int    x, y, z, width, depth, height ;
  double total_log_likelihood, log_likelihood ;

  width = mri_labels->width ;
  height = mri_labels->height ;
  depth = mri_labels->depth ;

  for (total_log_likelihood = 0.0, x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        log_likelihood =
          gcaVoxelGibbsLogLikelihood(gca, mri_labels,
                                     mri_inputs, x, y, z,transform,
                                     PRIOR_FACTOR);
        if (check_finite("gcaGibbsImageLoglikelihood",
                         log_likelihood) == 0)
          DiagBreak() ;
        total_log_likelihood += log_likelihood ;
        if (total_log_likelihood > 1e10)
          DiagBreak() ;
      }
    }
  }
  return(total_log_likelihood) ;
}
static double
gcaGibbsImpossibleConfiguration(GCA *gca, MRI *mri_labels,
                                int x, int y, int z, TRANSFORM *transform)
{
  int       xn, yn, zn, xnbr, ynbr, znbr, nbr_label, label, i,j, n;
  GCA_NODE  *gcan ;
  GC1D      *gc ;

  label = nint(MRIgetVoxVal(mri_labels, x, y, z, 0)) ;

  /* find the node associated with this coordinate and classify */
  if (!GCAsourceVoxelToNode(gca, mri_labels, transform,
                            x, y, z, &xn, &yn, &zn))
  {
    gcan = &gca->nodes[xn][yn][zn] ;

    for (n = 0 ; n < gcan->nlabels ; n++)
    {
      if (gcan->labels[n] == label)
        break ;
    }
    if (n >= gcan->nlabels)
      return(1) ;  /* never occurred */

    gc = &gcan->gcs[n] ;

    for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
    {
      xnbr = mri_labels->xi[x+xnbr_offset[i]] ;
      ynbr = mri_labels->yi[y+ynbr_offset[i]] ;
      znbr = mri_labels->zi[z+znbr_offset[i]] ;
      nbr_label = nint(MRIgetVoxVal(mri_labels, xnbr, ynbr, znbr,0)) ;
      for (j = 0 ; j < gc->nlabels[i] ; j++)
      {
        if (nbr_label == gc->labels[i][j])
          break ;
      }
      if (j < gc->nlabels[i])
      {
        if (FZERO(gc->label_priors[i][j]))
          return(1) ; /* never occurred (and this never should) */
      }
      else   /* never occurred - make it unlikely */
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        if (FZERO(gc->label_priors[i][j]))
          return(1) ; /* never occurred (and this never should) */
      }
    }
  }
  return(0) ;
}


static double
gcaNbhdGibbsLogLikelihood(GCA *gca,
                          MRI *mri_labels,
                          MRI *mri_inputs,
                          int x, int y, int z,
                          TRANSFORM *transform,
                          double gibbs_coef)
{
  double total_log_likelihood/*, log_likelihood*/ ;
  /*  int    i, xnbr, ynbr, znbr ;*/


  total_log_likelihood =
    gcaVoxelGibbsLogLikelihood(gca, mri_labels, mri_inputs, x, y, z, transform,
                               gibbs_coef) ;

#if 0
  for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
  {
    xnbr = mri_inputs->xi[x+xnbr_offset[i]] ;
    ynbr = mri_inputs->yi[y+ynbr_offset[i]] ;
    znbr = mri_inputs->zi[z+znbr_offset[i]] ;
    log_likelihood =
      gcaVoxelGibbsLogLikelihood(gca, mri_labels, mri_inputs, xnbr, ynbr,
                                 znbr, transform, gibbs_coef) ;
    total_log_likelihood += log_likelihood ;
  }
#endif

  if (check_finite("gcaNbhdGibbsLogLikelihood: final",
                   total_log_likelihood)  == 0)
    DiagBreak() ;

  return(total_log_likelihood) ;
}

static double
gcaVoxelGibbsLogLikelihood(GCA *gca,
                           MRI *mri_labels,
                           MRI *mri_inputs,
                           int x, int y, int z,
                           TRANSFORM *transform,
                           double gibbs_coef)
{
  double    log_likelihood/*, dist*/, nbr_prior ;
  int       xn, yn, zn, xnbr, ynbr, znbr, nbr_label, label,
  i,j, n;
  GCA_NODE  *gcan =0;
  GCA_PRIOR *gcap =0;
  GC1D      *gc =0;
  float     vals[MAX_GCA_INPUTS] ;
#if INTERP_PERIOR
  float       prior ;
#endif
  // float     tmp = 0;

  // signify error
  log_likelihood = 0.;

  // get the grey value
  load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
  // get the label
  label = nint(MRIgetVoxVal(mri_labels, x, y, z,0)) ;
  // what happens with higher number > CMA_MAX?
  /* find the node associated with this coordinate and classify */
  if (!GCAsourceVoxelToNode(gca, mri_inputs, transform,
                            x, y, z, &xn, &yn, &zn))
  {
    gcan = &gca->nodes[xn][yn][zn] ;
    gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
    if (gcap == NULL || gcap->nlabels <= 0)
    {
      if (label == Unknown)  // okay for there to be an
        // unknown label out of the fov
        return(0.0) ;
      else
        return (10*BIG_AND_NEGATIVE);
    }

    ////////////////// debug code (this should not occur ) /////////
    if (label > MAX_CMA_LABEL)
    {
      printf("\ngcaVoxelGibbsLogLikelihood() is called "
             "with label %d at (%d, %d, %d)\n", label, x, y, z);
      printf("gcan = %p, gcap = %p\n", gcan, gcap);
      if (gcan)
      {
        printf("gcan->nlabels = %d, gcan->total_training = %d ",
               gcan->nlabels, gcan->total_training);
        printf("log(return) = %.2f\n",
               log(0.01f/((float)
                          gcan->total_training*GIBBS_NEIGHBORS)));
        printf("labels for this location\n");
        for (n=0; n < gcan->nlabels; n++)
          printf("label=%s (%d); ",
                 cma_label_to_name(gcan->labels[n]), gcan->labels[n]);
      }
    }
    /////////////////////////////////////////////////////////////////
    for (n = 0 ; n < gcan->nlabels ; n++)
    {
      if (gcan->labels[n] == label)
        break ;
    }
    // could not find the label, then
    if (n >= gcan->nlabels)
    {
      // if (gcan->total_training > 0)
      // return(log(0.01f/((float)gcan->total_training*GIBBS_NEIGHBORS))) ;
      /* 10*GIBBS_NEIGHBORS*BIG_AND_NEGATIVE*/
      // else
      return (10*BIG_AND_NEGATIVE);
      //return(log(VERY_UNLIKELY)) ;
    }

    gc = &gcan->gcs[n] ;

    /* compute 1-d Mahalanobis distance */
    log_likelihood =
      GCAcomputeConditionalLogDensity(gc,vals,gca->ninputs, gcan->labels[n]);
    if (check_finite("gcaVoxelGibbsLogLikelihood: conditional log density",
                     log_likelihood) == 0)
      DiagBreak() ;

    nbr_prior = 0.0 ;
    for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
    {
      xnbr = mri_labels->xi[x+xnbr_offset[i]] ;
      ynbr = mri_labels->yi[y+ynbr_offset[i]] ;
      znbr = mri_labels->zi[z+znbr_offset[i]] ;
      nbr_label = nint(MRIgetVoxVal(mri_labels, xnbr, ynbr, znbr,0)) ;
      for (j = 0 ; j < gc->nlabels[i] ; j++)
      {
        if (nbr_label == gc->labels[i][j])
          break ;
      }
      if (j < gc->nlabels[i])
      {
        if (!FZERO(gc->label_priors[i][j]))
          nbr_prior += log(gc->label_priors[i][j]) ;
        else
          nbr_prior += log(0.1f/(float)gcan->total_training) ;
        /*BIG_AND_NEGATIVE */
        check_finite("gcaVoxelGibbsLogLikelihood: label_priors",
                     nbr_prior) ;
      }
      else   /* never occurred - make it unlikely */
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        nbr_prior += log(0.1f/(float)gcan->total_training) ;
        /*BIG_AND_NEGATIVE*/
      }
    }
    // added to the previous value
#if INTERP_PRIOR
    prior = gcaComputePrior(gca, mri_inputs, transform, x, y, z, label) ;
    log_likelihood += (gibbs_coef * nbr_prior + log(prior)) ;
#else
    log_likelihood += (gibbs_coef * nbr_prior + log(getPrior(gcap, label))) ;
#endif
    if (check_finite("gcaVoxelGibbsLogLikelihood: final",
                     log_likelihood)  == 0)
      DiagBreak() ;
  }
  else
  {
    return (10*BIG_AND_NEGATIVE);
    // return (log(VERY_UNLIKELY)) ;
  }

  // just check
#if 0
  tmp = log(0.01f/((float) gcan->total_training*GIBBS_NEIGHBORS));
  if (tmp > log_likelihood)
  {
    printf("gcaVoxelLogLikelihood: (%d, %d, %d)\n", x, y, z);
    printf("label %s(%d) log_likelihood %.2f is less"
           " than no label found %.2f\n",
           cma_label_to_name(label), label, log_likelihood, tmp);
  }
#endif
  return(log_likelihood) ;
}
// the likelihood of an image given a segmentation without any MRF
double
GCAimagePosteriorLogProbability(GCA *gca, MRI *mri_labels, MRI *mri_inputs, TRANSFORM *transform)
{
  double  log_likelihood ;
  int     x, y, z, num = 0 ;

  for (log_likelihood = 0.0, x = 0 ; x < mri_labels->width ; x++)
    for (y = 0 ; y < mri_labels->height ; y++)
      for (z = 0 ; z < mri_labels->depth ; z++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        if ((int)MRIgetVoxVal(mri_labels, x, y, z, 0) == 0)
          continue ;
        log_likelihood += gcaVoxelLogLikelihood(gca, 
                                                mri_labels, 
                                                mri_inputs, 
                                                x, y, z, 
                                                transform);
        num++ ;
        if (!finite(log_likelihood))
          DiagBreak() ;
      }
  return(log_likelihood/(float)num) ;
}

static double
gcaVoxelLogLikelihood(GCA *gca,
                      MRI *mri_labels,
                      MRI *mri_inputs,
                      int x, int y, int z,
                      TRANSFORM *transform)
{
  double    log_likelihood/*, dist*/ ;
  int       xn, yn, zn, label, n;
  GCA_NODE  *gcan =0;
  GCA_PRIOR *gcap =0;
  GC1D      *gc =0;
  float     vals[MAX_GCA_INPUTS] ;
#if INTERP_PERIOR
  float       prior ;
#endif
  // float     tmp = 0;

  // signify error
  log_likelihood = 0.;

  load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ; // get the grey value
  label = nint(MRIgetVoxVal(mri_labels, x, y, z, 0)) ; // get the label
  // what happens with higher number > CMA_MAX?
  /* find the node associated with this coordinate and classify */
  if (!GCAsourceVoxelToNode(gca, mri_inputs, transform,
                            x, y, z, &xn, &yn, &zn))
  {
    gcan = &gca->nodes[xn][yn][zn] ;
    gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
    if (gcap == NULL || gcap->nlabels <= 0)
    {
      if (label == Unknown)  // okay for there to be an
        return(0.0) ; // unknown label out of the fov
      else
        return (10*BIG_AND_NEGATIVE);
    }

    ////////////////// debug code (this should not occur ) /////////
    if (label > MAX_CMA_LABEL)
    {
      printf("\ngcaVoxelGibbsLogLikelihood() is called "
             "with label %d at (%d, %d, %d)\n", label, x, y, z);
      printf("gcan = %p, gcap = %p\n", gcan, gcap);
      if (gcan)
      {
        printf("gcan->nlabels = %d, gcan->total_training = %d ",
               gcan->nlabels, gcan->total_training);
        printf("log(return) = %.2f\n",
               log(0.01f/((float)
                          gcan->total_training*GIBBS_NEIGHBORS)));
        printf("labels for this location\n");
        for (n=0; n < gcan->nlabels; n++)
          printf("label=%s (%d); ",
                 cma_label_to_name(gcan->labels[n]), gcan->labels[n]);
      }
    }
    /////////////////////////////////////////////////////////////////
    for (n = 0 ; n < gcan->nlabels ; n++)
    {
      if (gcan->labels[n] == label)
        break ;
    }
    // could not find the label, then
    if (n >= gcan->nlabels)
    {
      // if (gcan->total_training > 0)
      // return(log(0.01f/((float)gcan->total_training*GIBBS_NEIGHBORS))) ;
      /* 10*GIBBS_NEIGHBORS*BIG_AND_NEGATIVE*/
      // else
      return (10*BIG_AND_NEGATIVE);
      //return(log(VERY_UNLIKELY)) ;
    }

    gc = &gcan->gcs[n] ;

    /* compute 1-d Mahalanobis distance */
    log_likelihood =
      GCAcomputeConditionalLogDensity(gc,vals,gca->ninputs, gcan->labels[n]);
    if (check_finite("gcaVoxelGibbsLogLikelihood: conditional log density",
                     log_likelihood) == 0)
      DiagBreak() ;

    // added to the previous value
#if INTERP_PRIOR
    prior = gcaComputePrior(gca, mri_inputs, transform, x, y, z, label) ;
    log_likelihood += (log(prior)) ;
#else
    log_likelihood += (log(getPrior(gcap, label))) ;
#endif
    if (check_finite("gcaVoxelGibbsLogLikelihood: final",
                     log_likelihood)  == 0)
      DiagBreak() ;
  }
  else
  {
    return (10*BIG_AND_NEGATIVE);
    // return (log(VERY_UNLIKELY)) ;
  }

  return(log_likelihood) ;
}

static int compare_sort_mri(const void *plp1, const void *plp2);
typedef struct
{
  unsigned char x, y, z, val ;
}
SORT_VOXEL ;

static int
MRIorderIndices(MRI *mri, short *x_indices, short *y_indices, short *z_indices)
{
  int         width, height, depth, nindices, index, x, y, z ;
  SORT_VOXEL  *sort_voxels ;

  width = mri->width, height = mri->height ;
  depth = mri->depth ;
  nindices = width*height*depth ;

  sort_voxels = (SORT_VOXEL *)calloc(nindices, sizeof(SORT_VOXEL)) ;
  if (!sort_voxels)
    ErrorExit(ERROR_NOMEMORY,"MRIorderIndices: could not allocate sort table");

  for (index = x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++, index++)
      {
        sort_voxels[index].x = x ;
        sort_voxels[index].y = y ;
        sort_voxels[index].z = z ;
        sort_voxels[index].val = MRIgetVoxVal(mri, x, y, z,0) ;
      }
    }
  }
  qsort(sort_voxels, nindices, sizeof(SORT_VOXEL), compare_sort_mri) ;

  for (index = 0 ; index < nindices ; index++)
  {
    x_indices[index] = sort_voxels[index].x ;
    y_indices[index] = sort_voxels[index].y ;
    z_indices[index] = sort_voxels[index].z ;
  }

  free(sort_voxels) ;
  return(NO_ERROR) ;
}

static int
compare_sort_mri(const void *psv1, const void *psv2)
{
  SORT_VOXEL  *sv1, *sv2 ;

  sv1 = (SORT_VOXEL *)psv1 ;
  sv2 = (SORT_VOXEL *)psv2 ;

  if (sv1->val > sv2->val)
    return(1) ;
  else if (sv1->val < sv2->val)
    return(-1) ;

  return(0) ;
}

MRI *
GCAbuildMostLikelyVolume(GCA *gca, MRI *mri)
{
  int       x,  y, z, xn, yn, zn, width, depth, height, n, xp, yp, zp, r ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  double    max_prior ;
  int       max_label ;
  GC1D      *gc_max ;

  if (!mri)
  {
    mri = MRIallocSequence(gca->prior_width, gca->prior_height,
                           gca->prior_depth, MRI_FLOAT, gca->ninputs) ;
    // hey create gca volume and thus copies gca prior values
    mri->xsize = gca->xsize*gca->prior_spacing ;
    mri->ysize = gca->ysize*gca->prior_spacing ;
    mri->zsize = gca->zsize*gca->prior_spacing ;
  }
  // most likely volume should agree with direction cosines
  GCAcopyDCToMRI(gca, mri);

  if (mri->nframes != gca->ninputs)
    ErrorExit(ERROR_BADPARM, "GCAbuildMostLikelyVolume: mri->frames "
              "(%d) does not match gca->ninputs (%d)",
              mri->nframes, gca->ninputs) ;


  // mri is prior if mri = NULL
  width = mri->width ;
  depth = mri->depth ;
  height = mri->height ;
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        // get node value
        if (GCAvoxelToNode(gca, mri, x, y, z, &xn, &yn, &zn) == NO_ERROR)
        {
          // get prior value
          if (GCAvoxelToPrior(gca, mri, x, y, z,
                              &xp, &yp, &zp) == NO_ERROR)
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            gcap = &gca->priors[xp][yp][zp] ;
            if (gcap==NULL || gcap->nlabels <= 0)
              continue;
            // initialize
            max_prior = gcap->priors[0] ;
            max_label = gcap->labels[0] ;
            gc_max = NULL ;
            // prior labels
            for (n = 1 ; n < gcap->nlabels ; n++)
            {
              if (gcap->priors[n] >= max_prior)
              {
                max_prior = gcap->priors[n] ;
                max_label = gcap->labels[n] ;
              }
            }
            // get max_prior, max_label
            // go through node labels
            for (n = 0 ; n < gcan->nlabels ; n++)
            {
              if (gcan->labels[n] == max_label)
                gc_max = &gcan->gcs[n] ;
            }

            if (!gc_max)
              continue ;
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              MRIsetVoxVal(mri, x, y, z, r, gc_max->means[r]) ;
            }
          }
          else
          {
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              MRIsetVoxVal(mri, x, y, z, r, 0) ;
            }
          }
        }
        else
        {
          for (r = 0 ; r < gca->ninputs ; r++)
          {
            MRIsetVoxVal(mri, x, y, z, r, 0) ;
          }
        }
      }
    }
  }

  return(mri) ;
}

MRI *
GCAbuildMostLikelyVolumeFrame(GCA *gca, MRI *mri, int frame)
{
  int       x,  y, z, xn, yn, zn, width, depth, height, n, xp, yp, zp ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  double    max_prior ;
  int       max_label ;
  GC1D      *gc_max ;

  if (!mri)
  {
    mri = MRIallocSequence(gca->prior_width, gca->prior_height,
                           gca->prior_depth, MRI_FLOAT, 1) ;
    mri->xsize = gca->xsize*gca->prior_spacing;
    mri->ysize = gca->ysize*gca->prior_spacing;
    mri->zsize = gca->zsize*gca->prior_spacing;
  }
  // gca volume direction cosines must be copied
  GCAcopyDCToMRI(gca, mri);

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

  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        if (!GCAvoxelToNode(gca, mri, x, y, z, &xn, &yn, &zn))
          if (!GCAvoxelToPrior(gca, mri, x, y, z, &xp, &yp, &zp))
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            gcap = &gca->priors[xp][yp][zp] ;
            if (gcap == NULL || gcap->nlabels <= 0)
              continue;
            if (gcap->nlabels ==0)
              continue;
            max_prior = gcap->priors[0] ;
            max_label = gcap->labels[0] ;
            gc_max = NULL ;
            for (n = 1 ; n < gcap->nlabels ; n++)
            {
              if (gcap->priors[n] >= max_prior)
              {
                max_prior = gcap->priors[n] ;
                max_label = gcap->labels[n] ;
              }
            }
            for (n = 0 ; n < gcan->nlabels ; n++)
            {
              if (gcan->labels[n] == max_label)
                gc_max = &gcan->gcs[n] ;
            }

            if (!gc_max)
              continue ;
            MRIsetVoxVal(mri, x, y, z, 0, gc_max->means[frame]) ;
          }
      }
    }
  }

  return(mri) ;
}

GC1D *
GCAfindPriorGC(GCA *gca, int xp, int yp, int zp,int label)
{
  int xn, yn, zn ;

  if (!GCApriorToNode(gca, xp, yp, zp, &xn, &yn, &zn))
    return(GCAfindGC(gca, xn, yn, zn, label)) ;
  else
    return NULL;
}

#if 1
GC1D *
GCAfindGC(GCA *gca, int xn, int yn, int zn,int label)
{
  int        n ;
  GCA_NODE   *gcan  ;

  gcan = &gca->nodes[xn][yn][zn] ;

  for (n = 0 ; n < gcan->nlabels ; n++)
  {
    if (gcan->labels[n] == label)
      return(&gcan->gcs[n]) ;
  }

  return(NULL) ;
}
#endif

#include "mrisegment.h"
/* each segment must be at least this much of total to be retained */
#define MIN_SEG_PCT  0.15

static int gcaReclassifySegment(GCA *gca, MRI *mri_inputs, MRI *mri_labels,
                                MRI_SEGMENT *mseg,
                                int old_label, TRANSFORM *transform);
static int gcaReclassifyVoxel(GCA *gca, MRI *mri_inputs, MRI *mri_labels,
                              int x, int y, int z,
                              int old_label, TRANSFORM *transform);
MRI *
GCAconstrainLabelTopology(GCA *gca, MRI *mri_inputs,MRI *mri_src, MRI *mri_dst,
                          TRANSFORM *transform)
{
  int              i, j, nvox; /*, x, y, z, width, height, depth*/
  ;
  MRI_SEGMENTATION *mriseg ;

  mri_dst = MRIcopy(mri_src, mri_dst) ;

  for (i = 1 ; i <= MAX_CMA_LABEL ; i++)
  {
    if (!IS_BRAIN(i)) // label is not brain, ignore
      continue ;
    // count number of label i in dst
    nvox = MRIvoxelsInLabel(mri_dst, i) ;
    // no such label, continue
    if (!nvox)
      continue ;
    // if hypointensities, continue
    if (LABEL_WITH_NO_TOPOLOGY_CONSTRAINT(i))
      continue ;
    /*    printf("label %03d: %d voxels\n", i, nvox) ;*/
    mriseg = MRIsegment(mri_src, (float)i, (float)i) ;
    if (!mriseg)
    {
      ErrorPrintf(Gerror,"GCAconstrainLabelTopology: "
                  "label %s failed (%d vox)",
                  cma_label_to_name(i), nvox) ;
      continue ;
    }

    /*    printf("\t%d segments:\n", mriseg->nsegments) ;*/
    for (j = 0 ; j < mriseg->nsegments ; j++)
    {
      if (IS_LAT_VENT(i) && mriseg->segments[j].nvoxels > 500)
        continue ;
      /* printf("\t\t%02d: %d voxels", j, mriseg->segments[j].nvoxels) ;*/
      if ((float)mriseg->segments[j].nvoxels / (float)nvox < MIN_SEG_PCT)
      {
        // printf(" - reclassifying...") ;
        gcaReclassifySegment(gca,mri_inputs,mri_dst,
                             &mriseg->segments[j], i,
                             transform);
      }
      // printf("\n") ;
    }
    MRIsegmentFree(&mriseg) ;
  }

#if 0
  width = mri_dst->width ;
  height = mri_dst->height ;
  depth  = mri_dst->depth ;
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == 144 && y == 118 && z == 127)
          DiagBreak() ;
        if (nint(MRIgetVoxVal(mri_dst, x, y, z,0)) == LABEL_UNDETERMINED)
          gcaReclassifyVoxel(gca, mri_inputs, mri_dst,
                             x, y, z, LABEL_UNDETERMINED, transform) ;
      }
    }
  }
#endif

  return(mri_dst) ;
}

static int
gcaReclassifySegment(GCA *gca, MRI *mri_inputs, MRI *mri_labels,
                     MRI_SEGMENT *mseg, int old_label, TRANSFORM *transform)
{
  int   i ;

  for (i = 0 ; i < mseg->nvoxels ; i++)
  {
#if 1
    gcaReclassifyVoxel(gca, mri_inputs, mri_labels,
                       mseg->voxels[i].x,
                       mseg->voxels[i].y,
                       mseg->voxels[i].z,
                       old_label, transform) ;
#else
    MRIsetVoxVal(mri_labels, 
								 mseg->voxels[i].x,
								 mseg->voxels[i].y,
								 mseg->voxels[i].z, 0
								 LABEL_UNDETERMINED ;
#endif
  }

  return(NO_ERROR) ;
}

static int
gcaReclassifyVoxel(GCA *gca, MRI *mri_inputs, MRI *mri_labels,
                   int x, int y, int z, int old_label, TRANSFORM *transform)
{
  int     nbr_labels[255], xi, yi, zi, xk, yk, zk, i, new_label ;
  double  max_p, p ;

  memset(nbr_labels, 0, sizeof(nbr_labels)) ;
  // get 6 neighbors
  for (zk = -1 ; zk <= 1 ; zk++)
  {
    zi = mri_labels->zi[z+zk] ;
    for (yk = -1 ; yk <= 1 ; yk++)
    {
      yi = mri_labels->yi[y+yk] ;
      for (xk = -1 ; xk <= 1 ; xk++)
      {
        xi = mri_labels->xi[x+xk] ;
        // get the label histogram
        nbr_labels[nint(MRIgetVoxVal(mri_labels, xi, yi, zi,0))]++ ;
      }
    }
  }
  new_label = 0 ;
  max_p = 10*BIG_AND_NEGATIVE ;
  nbr_labels[old_label] = 0 ; // don't look at old_label

  // for (i = 0 ; i <= 255 ; i++) // should not go up to 255 but MAX_CMA_LABEL
  for (i=0; i < MAX_CMA_LABEL; i++)
  {
    if (mri_labels->type == MRI_UCHAR && i >= 256)
      break ;
    if (nbr_labels[i] > 0)  // if neighbors has this label, then
    {
      MRIsetVoxVal(mri_labels, x, y, z, 0, i) ;
      // set to the current label and see what happens
      p = gcaVoxelGibbsLogLikelihood(gca, mri_labels, mri_inputs,
                                     x, y, z,transform,
                                     PRIOR_FACTOR);
      // debug ///////////////////////////////////////
      if (x==Ggca_x && y == Ggca_y && z == Ggca_z)
      {
        printf("gcaReclassifyVoxel: nbr_labels[%d] = %d\n",
               i, nbr_labels[i]);
        printf("gcaReclassifyVoxel:(%d, %d, %d): Label = %s (%d), "
               "VoxelGibbsLogLikelihood = %.2f, max_p = %.2f\n",
               x, y, z, cma_label_to_name(i), i, p, max_p);
        if (p >= max_p)
          printf("   replacing max_p with this p and "
                 "label from %s(%d) to %s(%d)\n",
                 cma_label_to_name(old_label), old_label,
                 cma_label_to_name(i), i);
      }
      ////////////////////////////////////////////////
      if (p >= max_p)
      {
        max_p = p ;
        new_label = i ;
      }
    }
  }
  MRIsetVoxVal(mri_labels, x, y, z, 0, new_label) ;
  return(NO_ERROR) ;
}
#define MAX_VENTRICLE_ITERATIONS  30
#define V_WSIZE (5)
#define V_WHALF (V_WSIZE-1)/2
#define V_THRESH ((V_WSIZE*V_WSIZE/2))
#if 1
MRI *
GCAexpandVentricle(GCA *gca, MRI *mri_inputs, MRI *mri_src,
                   MRI *mri_dst, TRANSFORM *transform, int target_label)
{
  int      nchanged, x, y, z, width, height, depth, xn, yn, zn, xk, yk, zk,
  xi, yi, zi, label, total_changed, i, j, count,
  found, xmin, xmax, ymin, ymax, zmin, zmax ;
  GCA_NODE *gcan ;
  GC1D     *gc ;
  float    v_means[MAX_GCA_INPUTS], v_var, dist_ven, 
    dist_label, vals[MAX_GCA_INPUTS] ;

  /* compute label mean and variance */

  GCAcomputeLabelStats(gca, target_label, &v_var, v_means) ;
  printf("ventricle intensity = ") ;
  for (i = 0 ; i < gca->ninputs ; i++)
    printf("%2.1f ", v_means[i]) ;
  printf(" +- %2.1f\n", sqrt(v_var)) ;


  if (mri_src != mri_dst)
    mri_dst = MRIcopy(mri_src, mri_dst) ;

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

  i = total_changed = 0 ;
  xmin = mri_dst->width ;
  ymin = mri_dst->height ;
  zmin = mri_dst->depth ;
  xmax = ymax = zmax = -1 ;
  for (z = 0 ; z < mri_dst->depth ; z++)
  {
    for (y = 0 ; y < mri_dst->height ; y++)
    {
      for (x = 0 ; x < mri_dst->width ; x++)
      {
        label = nint(MRIgetVoxVal(mri_dst, x, y, z,0)) ;
        if (label == target_label)
        {
          if (x > xmax)
            xmax = x ;
          if (y > ymax)
            ymax = y ;
          if (z > zmax)
            zmax = z ;
          if (x < xmin)
            xmin = x ;
          if (y < ymin)
            ymin = y ;
          if (z < zmin)
            zmin = z ;
        }
      }
    }
  }
  // expand bounding box to allow for ventricle to expand
  xmin = mri_dst->xi[xmin-1] ;
  ymin = mri_dst->yi[ymin-1] ;
  zmin = mri_dst->zi[zmin-1] ;
  xmax = mri_dst->xi[xmax+1] ;
  ymax = mri_dst->yi[ymax+1] ;
  zmax = mri_dst->zi[zmax+1] ;

  do
  {
    nchanged = 0 ;
    for (z = zmin ; z <= zmax ; z++)
    {
      for (y = ymin ; y <= ymax ; y++)
      {
        for (x = xmin ; x <= xmax ; x++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;
          label = nint(MRIgetVoxVal(mri_dst, x, y, z, 0)) ;
          if (label == target_label)
            continue ;
          found = 0 ;

          for (yk = -1 ; found == 0 && yk <= 1 ; yk += 2)
          {
            yi = mri_dst->yi[y+yk] ;  // look superior/inferior

            // only change it if there is a "wall" of 
            // ventricle superior or inferior
            count = MRIlabelsInPlanarNbhd
              (mri_dst, x, yi, z, V_WHALF, target_label, MRI_HORIZONTAL) ;
            if (count >= V_THRESH)
              found = 1 ;
          }
          for (xk = -1 ; found == 0 && xk <= 1 ; xk += 2)
          {
            xi = mri_dst->xi[x+xk] ;  // look superior/inferior

            // only change it if there is a "wall" of 
            // ventricle superior or inferior
            count = MRIlabelsInPlanarNbhd
              (mri_dst, xi, y, z, V_WHALF, target_label, MRI_SAGITTAL) ;
            if (count >= V_THRESH)
              found = 1 ;
          }
          for (zk = -1 ; found == 0 && zk <= 1 ; zk += 2)
          {
            zi = mri_dst->zi[z+zk] ;  // look superior/inferior

            // only change it if there is a "wall" of 
            // ventricle anterior or posterior
            count = MRIlabelsInPlanarNbhd
              (mri_dst, x, y, zi, V_WHALF, target_label, MRI_CORONAL) ;
            if (count >= V_THRESH)
              found = 1 ;
          }
          if (found == 0)
            continue ;
          if (GCAsourceVoxelToNode(gca, mri_dst,transform,x, y, z,
                                   &xn, &yn, &zn) == NO_ERROR)
          {
            gcan = &gca->nodes[xn][yn][zn] ;

            if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
              DiagBreak() ;

            load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
            for (dist_ven = 0, j = 0 ; j < gca->ninputs ; j++)
              dist_ven += SQR(vals[j]-v_means[j]);
            gc = GCAfindGC(gca, xn, yn, zn, label) ;
            if (gc == NULL)
              dist_label = 10000 ; // ???
            else
              dist_label = GCAmahDistIdentityCovariance(gc, vals,gca->ninputs);
            gc = GCAfindGC(gca, xn, yn, zn,target_label) ;
            if (2*dist_ven < dist_label)   // much more like ventricle 
                                           //than anything else
            {
              if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
              {
                int olabel = nint(MRIgetVoxVal(mri_dst, x, y, z,0)) ;
                printf("GCAexpandVentricle:voxel"
                       "(%d, %d, %d) changed from %s (%d) "
                       "to %s (%d), because current label d = %2.0f "
                       "and new lable d = %2.0f\n",
                       x, y, z,
                       cma_label_to_name(olabel), olabel,
                       cma_label_to_name(target_label), target_label,
                       dist_label, dist_ven);
              }
              // change it to ventricle
              nchanged++ ;
              MRIsetVoxVal(mri_dst, x, y, z, target_label, 0) ;
              if (x <= xmin)
                xmin = mri_dst->xi[x-1] ;
              if (y <= ymin)
                ymin = mri_dst->yi[y-1] ;
              if (z <= zmin)
                zmin = mri_dst->zi[z-1] ;
              if (x >= xmax)
                xmax = mri_dst->xi[x+1] ;
              if (y >= ymax)
                ymax = mri_dst->yi[y+1] ;
              if (z >= zmax)
                zmax = mri_dst->zi[z+1] ;
            }
          }
        }
      }
    }

    total_changed += nchanged ;
    if (++i > MAX_VENTRICLE_ITERATIONS)
      break ;
  }
  while (nchanged > 0) ;

  printf("%d labels changed to %s...\n",
         total_changed, cma_label_to_name(target_label)) ;
  return(mri_dst) ;
}
#else
MRI *
GCAexpandVentricle(GCA *gca, MRI *mri_inputs, MRI *mri_src,
                   MRI *mri_dst, TRANSFORM *transform, int target_label)
{
  int      nchanged, x, y, z, width, height, depth, xn, yn, zn, xi, yi, zi,
  xk, yk, zk, label, total_changed, i ;
  GCA_NODE *gcan ;
  GC1D     *gc, *gc_vent ;
  float    v_means[MAX_GCA_INPUTS], v_var, pv, plabel, vals[MAX_GCA_INPUTS] ;
  MRI      *mri_tmp ;

  /* compute label mean and variance */

  GCAcomputeLabelStats(gca, target_label, &v_var, v_means) ;
  printf("ventricle intensity = ") ;
  for (i = 0 ; i < gca->ninputs ; i++)
    printf("%2.1f ", v_means[i]) ;
  printf(" +- %2.1f\n", sqrt(v_var)) ;


  if (mri_src != mri_dst)
    mri_dst = MRIcopy(mri_src, mri_dst) ;

  mri_tmp = MRIcopy(mri_dst, NULL) ;

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

  i = total_changed = 0 ;
  do
  {
    nchanged = 0 ;
    for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (x = 0 ; x < width ; x++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;

          if (!GCAsourceVoxelToNode(gca, mri_dst, transform,
                                    x, y, z, &xn, &yn, &zn))
          {
            gc_vent = GCAfindGC(gca, xn, yn, zn, target_label) ;
            if (gc_vent == NULL)
              continue ;
            label = nint(MRIgetVoxVal(mri_dst, x, y, z)) ;

            if (label == target_label)
            {
              for (zk = -1 ; zk <= 1 ; zk++)
              {
                for (yk = -1 ; yk <= 1 ; yk++)
                {
                  for (xk = -1 ; xk <= 1 ; xk++)
                  {
                    if (fabs(xk) + fabs(yk) + fabs(zk) > 1)
                      continue ;
                    xi = mri_src->xi[x+xk] ;
                    yi = mri_src->yi[y+yk] ;
                    zi = mri_src->zi[z+zk] ;
                    label = nint(MRIgetVoxVal(mri_dst, xi, yi, zi, 0)) ;
                    if (label != target_label)
                      /* should it be changed? */
                    {
                      if (!GCAsourceVoxelToNode(gca,
                                                mri_dst,
                                                transform,
                                                xi, yi, zi,
                                                &xn,
                                                &yn,
                                                &zn))
                      {
                        gcan = &gca->nodes[xn][yn][zn] ;

                        if (xi == Ggca_x
                            && yi == Ggca_y
                            && zi == Ggca_z)
                          DiagBreak() ;

                        load_vals(mri_inputs,
                                  xi, yi, zi,
                                  vals, gca->ninputs) ;
                        gc = GCAfindGC(gca,
                                       xn, yn, zn,
                                       label) ;
                        if (gc == NULL)
                          plabel = 0.0 ;
                        else
                          plabel =
                            GCAcomputeConditionalDensity
                            (gc,
                             vals,
                             gca->ninputs,
                             label) ;
                        gc = GCAfindGC(gca,
                                       xn, yn, zn,
                                       target_label) ;
                        if (gc == NULL)
                          /* use neighboring gc */
                          /*to allow for variability */
                          /*in ventricles */
                          gc = gc_vent ;
                        pv =
                          GCAcomputeConditionalDensity
                          (gc,
                           vals,
                           gca->ninputs,
                           target_label);
                        if (pv > 5*plabel)
                        {
                          if (xi == Ggca_x
                              && yi == Ggca_y
                              && zi == Ggca_z)
                          {
                            int olabel =
                              nint(MRIgetVoxVal(mri_tmp,xi, yi, zi,0)) ;
                            printf("GCAexpandVentricle:voxel"
                                   "(%d, %d, %d) changed from %s (%d) "
                                   "to %s (%d), because current "
                                   "label p = %.2f "
                                   "and new lable p = %.2f\n",
                                   xi, yi, zi,
                                   cma_label_to_name(olabel), olabel,
                                   cma_label_to_name(target_label), 
                                   target_label,
                                   plabel, pv);
                          }
                          if (xi == 140
                              && yi == 79
                              && zi == 136)
                            DiagBreak() ;
                          /* v should be wm */
                          nchanged++ ;
                          MRIsetVoxVal(mri_tmp, xi, yi, zi, 0,target_label) ;
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    MRIcopy(mri_tmp, mri_dst) ;
    total_changed += nchanged ;
    if (++i > MAX_VENTRICLE_ITERATIONS)
      break ;
  }
  while (nchanged > 0) ;

  MRIfree(&mri_tmp) ;
  printf("%d labels changed to %s...\n",
         total_changed, cma_label_to_name(target_label)) ;
  return(mri_dst) ;
}
#endif
#define MAX_CORTICAL_ITERATIONS  10
MRI *
GCAexpandCortex(GCA *gca, MRI *mri_inputs, MRI *mri_src,
                MRI *mri_dst, TRANSFORM *transform)
{
  int      nchanged, x, y, z, width, height, depth, xn, yn, zn, xi, yi, zi,
  xk, yk, zk, label, total_changed, i, wm_nbr, gray_nbr, left ;
  GCA_NODE *gcan ;
  float    wm_means[MAX_GCA_INPUTS],\
  wm_var, gray_means[MAX_GCA_INPUTS], gray_var, ldist, wdist, gdist ;
  MRI      *mri_tmp ;
  float    vals[MAX_GCA_INPUTS] ;
  GC1D     *gc_wm, *gc_gm, *gc_label ;


  /* compute label mean and variance */

  GCAcomputeLabelStats(gca, Left_Cerebral_White_Matter, &wm_var, wm_means) ;
  GCAcomputeLabelStats(gca, Left_Cerebral_Cortex, &gray_var, gray_means) ;
  printf("cortex mean - gray ") ;
  for (i = 0 ; i < gca->ninputs ; i++)
    printf("%2.1f ", gray_means[i]) ;
  printf("+- %2.1f, white ", sqrt(gray_var)) ;
  for (i = 0 ; i < gca->ninputs ; i++)
    printf("%2.1f ", wm_means[i]) ;
  printf("+- %2.1f\n", sqrt(wm_var)) ;

  if (mri_src != mri_dst)
    mri_dst = MRIcopy(mri_src, mri_dst) ;

  mri_tmp = MRIcopy(mri_dst, NULL) ;

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

  i = total_changed = 0 ;
  do
  {
    nchanged = 0 ;
    for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (x = 0 ; x < width ; x++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;

          label = nint(MRIgetVoxVal(mri_dst, x, y, z,0)) ;

          if (label == Unknown ||
              label == Left_Cerebral_Cortex ||
              label == Right_Cerebral_Cortex ||
              label == Left_Cerebral_White_Matter ||
              label == Right_Cerebral_White_Matter
             )
          {
            if (label != Unknown)
              left = (label == Left_Cerebral_Cortex || \
                      label == Left_Cerebral_White_Matter) ;
            else
            {
              left = -1 ;
              for (zk = -1 ; left < 0 && zk <= 1 ; zk++)
              {
                for (yk = -1 ; left < 0 && yk <= 1 ; yk++)
                {
                  for (xk = -1 ; left < 0 && xk <= 1 ; xk++)
                  {
                    xi = mri_src->xi[x+xk] ;
                    yi = mri_src->yi[y+yk] ;
                    zi = mri_src->zi[z+zk] ;
                    label = nint(MRIgetVoxVal(mri_dst, xi, yi, zi,0)) ;
                    if (label == Left_Cerebral_Cortex || \
                        label == Left_Cerebral_White_Matter)
                      left = 1 ;
                    else if (label ==
                             Right_Cerebral_Cortex
                             || \
                             label ==
                             Right_Cerebral_White_Matter)
                      left = 0 ;
                  }
                }
              }
            }

            gray_nbr = wm_nbr = 0 ;
            gc_wm = GCAfindSourceGC(gca, mri_src, transform,
                                    x, y, z,
                                    left ? \
                                    Left_Cerebral_White_Matter : \
                                    Right_Cerebral_White_Matter) ;
            gc_gm = GCAfindSourceGC(gca, mri_src, transform,
                                    x, y, z,
                                    left ? \
                                    Left_Cerebral_Cortex : \
                                    Right_Cerebral_Cortex) ;
            if (gc_gm)
              gray_nbr = left ? Left_Cerebral_Cortex :
                         Right_Cerebral_Cortex ;
            if (gc_wm)
              wm_nbr = left ? Left_Cerebral_White_Matter : \
                       Right_Cerebral_White_Matter ;
            if (gc_gm == NULL || gc_wm == NULL)
              for (zk = -1 ; zk <= 1 ; zk++)
              {
                for (yk = -1 ; yk <= 1 ; yk++)
                {
                  for (xk = -1 ; xk <= 1 ; xk++)
                  {
                    if (fabs(xk) + fabs(yk) + fabs(zk) > 1)
                      continue ;
                    xi = mri_src->xi[x+xk] ;
                    yi = mri_src->yi[y+yk] ;
                    zi = mri_src->zi[z+zk] ;
                    label = nint(MRIgetVoxVal(mri_dst, xi, yi, zi, 0)) ;
                    if ((label ==  Left_Cerebral_Cortex ||
                         label ==  Right_Cerebral_Cortex) && \
                        (gc_gm == NULL))
                    {
                      gray_nbr = label ;
                      gc_gm = GCAfindSourceGC(gca, mri_src,
                                              transform,
                                              xi, yi, zi,
                                              label) ;
                      if (!gc_gm)
                        gray_nbr = 0 ;
                      /* shouldn't ever happen */
                    }
                    else
                      if ((label ==
                           Left_Cerebral_White_Matter
                           ||
                           label ==
                           Right_Cerebral_White_Matter)
                          && (gc_wm == NULL))
                      {
                        wm_nbr = label ;
                        gc_wm = GCAfindSourceGC(gca,
                                                mri_src,
                                                transform,
                                                xi, yi, zi,
                                                label) ;
                        if (!gc_wm)
                          wm_nbr = 0 ;
                        /* shouldn't ever happen */
                      }
                  }
                }
              }
            if (!wm_nbr && !gray_nbr)
              continue ;

            load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
            if (wm_nbr)
              wdist =
                sqrt(GCAmahDistIdentityCovariance(gc_wm, vals,
                                                  gca->ninputs)) ;
            else
              wdist = 1e10 ;
            if (gray_nbr)
              gdist =
                sqrt(GCAmahDistIdentityCovariance(gc_gm, vals,
                                                  gca->ninputs)) ;
            else
              gdist = 1e10 ;

            if (gc_wm == NULL || \
                sqrt(GCAmahDist(gc_wm, vals, gca->ninputs)) > 1.5)
              wdist = 1e10 ; /* hack - don't label unlikely white */
            if (gc_gm == NULL || \
                sqrt(GCAmahDist(gc_gm, vals, gca->ninputs)) > 1.5)
              gdist = 1e10 ;  /* hack - don't label unlikely gray */

            if (!GCAsourceVoxelToNode(gca, mri_dst, \
                                      transform, x, y, z,
                                      &xn, &yn, &zn))
            {
              gcan = &gca->nodes[xn][yn][zn] ;
              gc_label = GCAfindGC(gca, xn, yn, zn, label) ;
              if (gc_label)
                ldist =
                  sqrt(GCAmahDistIdentityCovariance(gc_label, \
                                                    vals,
                                                    gca->ninputs));
              else
                ldist = 1e10 ;
              ldist *= .75 ;  /* bias towards retaining label */

              if ((wdist < gdist) && gc_wm)
                /* might change to wm */
              {
                if (wdist < ldist && GCAisPossible(gca, \
                                                   mri_inputs,
                                                   wm_nbr,
                                                   transform,
                                                   x, y, z, 0))
                {
                  if (x == Ggca_x
                      && y == Ggca_y
                      && z == Ggca_z)
                  {
                    int olabel = nint(MRIgetVoxVal(mri_tmp, x, y, z, 0)) ;
                    if (olabel != wm_nbr)
                      printf(
                        "GCAexpandCortex:voxel (%d, %d, %d)"
                        " changed from %s (%d) "
                        "to %s (%d), wdist=%.2f, gdist=%.2f,"
                        " ldist=%.2f\n", x, y, z,
                        cma_label_to_name(olabel), olabel,
                        cma_label_to_name(wm_nbr), wm_nbr,
                        wdist, gdist, ldist);
                  }
                  nchanged++ ;
                  MRIsetVoxVal(mri_tmp, x, y, z, 0, wm_nbr) ;
                }
              }
              else if (gc_gm)            /* might change to gm */
              {
                if (gdist < ldist && GCAisPossible(gca,
                                                   mri_inputs,
                                                   gray_nbr,
                                                   transform,
                                                   x, y, z, 0))
                {
                  int olabel = nint(MRIgetVoxVal(mri_tmp, x, y, z,0)) ;
                  if (x == Ggca_x
                      && y == Ggca_y
                      && z == Ggca_z)
                  {
                    if (olabel != gray_nbr)
                      printf
                      (
                        "GCAexpandCortex:voxel "
                        "(%d, %d, %d) changed from %s (%d) "
                        "to %s (%d), wdist=%.2f, "
                        "gdist=%.2f, ldist=%.2f\n",
                        x, y, z,
                        cma_label_to_name(olabel), olabel,
                        cma_label_to_name(gray_nbr),
                        gray_nbr,
                        wdist, gdist, ldist);
                  }
                  if (olabel != gray_nbr)
                    nchanged++ ;

                  MRIsetVoxVal(mri_tmp, x, y, z, 0, gray_nbr) ;
                }
              }
            }
          }
        }
      }
    }
    MRIcopy(mri_tmp, mri_dst) ;
    total_changed += nchanged ;
    if (++i > MAX_CORTICAL_ITERATIONS)
      break ;
  }
  while (nchanged > 0) ;

  MRIfree(&mri_tmp) ;
  printf("%d labels changed to cortex...\n", total_changed) ;
  return(mri_dst) ;
}

MRI   *
GCAexpandLabelIntoWM(GCA *gca, MRI *mri_inputs, MRI *mri_src,
                     MRI *mri_dst,
                     TRANSFORM *transform, MRI *mri_fixed, int target_label)
{
  int      nchanged, x, y, z, width, height, depth, xn, yn, zn, xi, yi, zi,
  xk, yk, zk, nbr_label, n, label, total_changed, i ;
  GCA_NODE *gcan, *gcan_nbr ;
  GC1D     *gc_label, *gc_wm ;
  MRI      *mri_tmp ;
  float    vals[MAX_GCA_INPUTS] ;
  double   prior ;

  if (mri_src != mri_dst)
    mri_dst = MRIcopy(mri_src, mri_dst) ;

  mri_tmp = MRIcopy(mri_dst, NULL) ;

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

  i = total_changed = 0 ;
  do
  {
    nchanged = 0 ;
    for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (x = 0 ; x < width ; x++)
        {
#if 0
          if (x == 140 && y == 111 && z == 139)
            DiagBreak() ;  /* should be wm */
          if (x == 138 && y == 103 && z == 139)
            DiagBreak() ;  /* should be pallidum */
#endif
          // get the label
          label = nint(MRIgetVoxVal(mri_dst, x, y, z,0)) ;

          if (!GCAsourceVoxelToNode(gca, mri_dst,
                                    transform, x, y, z, &xn, &yn, &zn))
          {
            gcan = &gca->nodes[xn][yn][zn] ;

            // if this label is the same as the target label,
            // then expand
            // into neighbors if conditions are satisfied.
            if (label == target_label)
            {
              gc_label = GCAfindGC(gca, xn, yn, zn, label) ;
              // find wm gaussian classifier
              gc_wm = NULL ;
              for (n = 0 ; n < gcan->nlabels ; n++)
              {
                if
                ((gcan->labels[n] ==
                    Left_Cerebral_White_Matter) ||
                    (gcan->labels[n] ==
                     Right_Cerebral_White_Matter))
                  gc_wm = GCAfindGC(gca, xn, yn, zn,
                                    gcan->labels[n]) ;
              }
              // look around the neighbors
              for (zk = -1 ; zk <= 1 ; zk++)
              {
                for (yk = -1 ; yk <= 1 ; yk++)
                {
                  for (xk = -1 ; xk <= 1 ; xk++)
                  {
                    if (fabs(xk) + fabs(yk) + fabs(zk) > 1)
                      continue ;
                    xi = mri_src->xi[x+xk] ;
                    yi = mri_src->yi[y+yk] ;
                    zi = mri_src->zi[z+zk] ;
                    if (xi == Ggca_x
                        && yi == Ggca_y
                        && zi == Ggca_z)
                      DiagBreak() ;
                    // get the neighbor label
                    nbr_label = nint(MRIgetVoxVal(mri_dst, xi, yi, zi,0)) ;
                    if ((nbr_label ==
                         Right_Cerebral_White_Matter) ||
                        (nbr_label ==
                         Left_Cerebral_White_Matter))
                    {
                      // if it is wm, then load
                      //grey values at this neighbor
                      load_vals(mri_inputs,
                                xi, yi, zi,
                                vals, gca->ninputs) ;
                      if (!GCAsourceVoxelToNode(gca,
                                                mri_dst,
                                                transform,
                                                xi, yi, zi,
                                                &xn,
                                                &yn,
                                                &zn))
                      {
                        // get the wm gc
                        gc_wm = GCAfindGC(gca,
                                          xn, yn, zn,
                                          nbr_label) ;
                        // get the target label gc
                        gc_label =
                          GCAfindGC(gca,
                                    xn, yn, zn,
                                    target_label);
                        if (!gc_wm || !gc_label)
                          continue ;
                        // calculate distance
                        // to wm and target label.
                        // if wm is bigger
                        if (GCAmahDistIdentityCovariance\
                            (gc_wm,
                             vals,
                             gca->ninputs) >
                            GCAmahDistIdentityCovariance\
                            (gc_label,
                             vals,
                             gca->ninputs))
                        {
                          gcan_nbr =
                            &gca->nodes[xn][yn][zn] ;
                          for (prior = 0.0f, n = 0;
                               n < gcan_nbr->nlabels;
                               n++)
                          {
                            // look for the
                            // target label
                            // in this neighbor
                            if (gcan_nbr->labels[n]
                                == \
                                target_label)
                            {
                              prior =
                                get_node_prior
                                (gca,
                                 target_label,
                                 xn, yn, zn) ;
                              if (prior != 0)
                                break ;
                            }
                          }
                          // if not found,
                          // prior stays 0.0f
#define PRIOR_THRESH 0.01
                          if (prior >= PRIOR_THRESH)
                            /* target is possible */
                          {
                            if (x == Ggca_x
                                &&
                                y == Ggca_y
                                &&
                                z == Ggca_z)
                            {
                              printf("GCAexpandLabelIntoWM:voxel (%d, %d, %d) "
                                     "changed from %s (%d) "
                                     "to %s (%d), prior=%.2f\n",
                                     xi, yi, zi,
                                     cma_label_to_name(nbr_label), nbr_label,
                                     cma_label_to_name(target_label), 
                                     target_label,
                                     prior);
                            }
                            nchanged++ ;
                            MRIsetVoxVal(mri_tmp, xi, yi, zi, 0, target_label);
                            /* MRIvox(mri_fixed,
                               xi, yi, zi) = 0 ;*/
                          }
                        }
                      }
                      ///////////////////
                    }
                  }
                }
              }
            }
          }
          /////////////////////////////////////////////
        }
      }
    }
    MRIcopy(mri_tmp, mri_dst) ;
    total_changed += nchanged ;
    if (++i >= 1)
      break ;
  }
  while (nchanged > 0) ;

  MRIfree(&mri_tmp) ;
  printf("%d labels changed to %s...\n",
         total_changed, cma_label_to_name(target_label)) ;
  return(mri_dst) ;
}


int
GCArankSamples(GCA *gca, GCA_SAMPLE *gcas, int nsamples, int *ordered_indices)
{
  LABEL_PROB  *label_probs ;
  int         i ;

  label_probs = (LABEL_PROB *)calloc(nsamples, sizeof(LABEL_PROB)) ;
  for (i = 0 ; i < nsamples ; i++)
  {
    label_probs[i].label = i ;
    label_probs[i].prob =  gcas[i].log_p ;
  }

  /* now sort the samples by probability */
  qsort(label_probs, nsamples, sizeof(LABEL_PROB),
        compare_sort_probabilities) ;

  for (i = 0 ; i < nsamples ; i++)
  {
    ordered_indices[i] = label_probs[nsamples-(i+1)].label ;
  }

  free(label_probs) ;
  return(NO_ERROR) ;
}

#include "mrinorm.h"
MRI *
GCAnormalizeSamples(MRI *mri_in, GCA *gca, GCA_SAMPLE *gcas, int nsamples,
                    TRANSFORM *transform, char *ctl_point_fname)
{
  MRI    *mri_dst, *mri_ctrl, *mri_bias ;
  int    xv, yv, zv, n, x, y, z,
  width, height, depth, xn, yn, zn, num, total, input,
  T1_index = 0 ;
  float   bias ;
  double  mean, sigma ;
  Real    val ;
  float      gm_means[MAX_GCA_INPUTS], gray_white_CNR;

  if (nsamples == 0)   /* only using control points from file */
  {
    float      wm_means[MAX_GCA_INPUTS], tmp[MAX_GCA_INPUTS], max_wm ;
    int        r ;

    GCAlabelMean(gca, Left_Cerebral_White_Matter, wm_means) ;
    //    GCAlabelMean(gca, Left_Cerebral_White_Matter, tmp) ;
    GCAlabelMean(gca, Right_Cerebral_White_Matter, tmp) ;

#if 0
    max_wm = 0 ;
    for (r = 0 ; r < gca->ninputs ; r++)
    {
      wm_means[r] = (wm_means[r] + tmp[r]) / 2 ;
      if (wm_means[r] > max_wm)
      {
        T1_index = r ;
        max_wm = wm_means[r] ;
      }
    }
#else
    GCAlabelMean(gca, Left_Cerebral_Cortex, gm_means) ;
    gray_white_CNR = wm_means[0] - gm_means[0];
    for (r = 0 ; r < gca->ninputs ; r++)
    {
      wm_means[r] = (wm_means[r] + tmp[r]) / 2 ;
      if ((wm_means[r] - gm_means[r]) > gray_white_CNR)
      {
        T1_index = r ;
        gray_white_CNR = (wm_means[r] - gm_means[r]);
      }
    }
    max_wm = wm_means[T1_index];
#endif
    printf("using volume %d as most T1-weighted for normalization\n",
           T1_index) ;
  }
  width = mri_in->width ;
  height = mri_in->height ;
  depth = mri_in->depth ;
  mri_dst = MRIclone(mri_in, NULL) ;
  mri_ctrl = MRIalloc(width, height, depth, MRI_UCHAR) ;
  MRIcopyHeader(mri_in, mri_ctrl);
  mri_bias = MRIalloc(mri_in->width,mri_in->height,mri_in->depth,MRI_SHORT);
  if (!mri_bias)
    ErrorExit(ERROR_NOMEMORY,
              "GCAnormalizeSamples: could not allocate "
              "(%d,%d,%d,2) bias image",
              mri_in->width,mri_in->height,mri_in->depth) ;
  MRIcopyHeader(mri_in, mri_bias);

#define MAX_BIAS 1250
#define NO_BIAS  1000
#define MIN_BIAS  750

  if (ctl_point_fname)
  {
    MRI3dUseFileControlPoints(mri_ctrl, ctl_point_fname) ;
    MRInormAddFileControlPoints(mri_ctrl, CONTROL_MARKED) ;
  }

  /* add control points from file */
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        MRISvox(mri_bias, x,y,z) = NO_BIAS ;  /* by default */
        if (MRIvox(mri_ctrl, x, y, z) != CONTROL_MARKED)
          /* not read from file */
          continue ;

        if (nsamples == 0)   /* only using file control points */
        {
          MRIsampleVolumeFrame(mri_in, x, y, z, T1_index, &val) ;
          bias = NO_BIAS*DEFAULT_DESIRED_WHITE_MATTER_VALUE / val ;
          MRISvox(mri_bias, x, y, z) = (short)nint(bias) ;
        }
        else    /* find atlas point this maps to */
        {
          int       n, max_n ;
          GC1D      *gc ;
          GCA_NODE  *gcan ;
          GCA_PRIOR *gcap ;
          double    max_p ;

          if (!GCAsourceVoxelToNode(gca, mri_dst,
                                    transform,
                                    x, y, z, &xn, &yn, &zn))
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            gcap = getGCAP(gca, mri_dst, transform, x, y, z) ;
            if (gcap==NULL)
              continue;
            max_p = 0 ;
            for (max_n = -1, n = 0 ; n < gcan->nlabels ; n++)
            {
              if ((0 == IS_WM(gcan->labels[n])) &&
                  (0 == IS_CEREBELLAR_WM(gcan->labels[n])) &&
                  (gcan->labels[n] != Brain_Stem))
                continue ;
              gc = &gcan->gcs[n] ;
              if (getPrior(gcap, gcan->labels[n]) >= max_p)
              {
                max_p = getPrior(gcap, gcan->labels[n]) ;
                max_n = n ;
              }
            }
            if (max_n < 0)
              /* couldn't find any valid label at this location */
              continue ;
            gc = &gcan->gcs[max_n] ;

            for (bias = 0.0, input = 0 ;
                 input < gca->ninputs ;
                 input++)
            {
              MRIsampleVolumeFrame(mri_in, x, y, z, input, &val) ;
              if (FZERO(val))
                val = 1 ;
              bias += (float)NO_BIAS*((float)gc->means[input]/val);
            }
            bias /= (float)gca->ninputs ;
            if (bias < 100 || bias > 5000)
              DiagBreak() ;
            if (bias < MIN_BIAS)
              bias = MIN_BIAS ;
            if (bias > MAX_BIAS)
              bias = MAX_BIAS ;

            MRISvox(mri_bias, x, y, z) = (short)nint(bias) ;
          }
          /////////////////////////////////////////////////////
        }
      }
    }
  }

  TransformInvert(transform, mri_in) ;
  for (n = 0 ; n < nsamples ; n++)
  {
    if (gcas[n].xp == Gxp && gcas[n].yp == Gyp && gcas[n].zp == Gzp)
      DiagBreak() ;

    if (!GCApriorToSourceVoxel(gca, mri_dst, transform,
                               gcas[n].xp, gcas[n].yp, gcas[n].zp,
                               &xv, &yv, &zv))
    {
      if (xv == 181 && yv == 146 && zv == 128)
        DiagBreak() ;
      if (xv == Ggca_x && yv == Ggca_y && zv == Ggca_z)
        DiagBreak() ;
      if (gcas[n].label == 29 || gcas[n].label == 61)
      {
        gcas[n].label = 0 ;
        DiagBreak() ;
      }
      if (gcas[n].label > 0)
      {
        MRIvox(mri_ctrl, xv, yv, zv) = CONTROL_MARKED ;

        for (bias = 0.0, input = 0 ; input < gca->ninputs ; input++)
        {
          MRIsampleVolumeFrame(mri_in, xv, yv, zv, input, &val) ;
          if (FZERO(val))
            val = 1 ;
          bias += (float)NO_BIAS*((float)gcas[n].means[input]/val) ;
        }
        bias /= (float)gca->ninputs ;
        if (bias < 100 || bias > 5000)
          DiagBreak() ;
#if 0
        if (bias < MIN_BIAS)
          bias = MIN_BIAS ;
        if (bias > MAX_BIAS)
          bias = MAX_BIAS ;
#endif

        MRISvox(mri_bias, xv, yv, zv) = (short)nint(bias) ;
      }
      else
        MRIvox(mri_ctrl, xv, yv, zv) = CONTROL_NONE ;
    }
  }

  /* now check for and remove outliers */
  mean = sigma = 0.0 ;
  for (num = z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        if (MRIvox(mri_ctrl, x, y, z) == CONTROL_MARKED)
        {
          num++ ;
          bias = (double)MRISvox(mri_bias, x, y, z) ;
          mean += bias ;
          sigma += (bias*bias) ;
        }
      }
    }
  }

  if (num > 0)
  {
    mean /= (double)num ;
    sigma  = sqrt(sigma / (double)num - mean*mean) ;
    printf("bias field = %2.3f +- %2.3f\n", mean/NO_BIAS, sigma/NO_BIAS) ;
  }

  /* now check for and remove outliers */
  for (total = num = z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        if (MRIvox(mri_ctrl, x, y, z) == CONTROL_MARKED)
        {
          bias = (double)MRISvox(mri_bias, x, y, z) ;
          total++ ;
          if (fabs(bias-mean) > 4*sigma)
          {
            MRIvox(mri_ctrl, x, y, z) = CONTROL_NONE ;
            num++ ;
            MRISvox(mri_bias, x, y, z) = NO_BIAS ;
          }
        }
      }
    }
  }

  printf("%d of %d control points discarded\n", num, total) ;

  MRIbuildVoronoiDiagram(mri_bias, mri_ctrl, mri_bias) ;
  /*  MRIwrite(mri_bias, "bias.mgz") ;*/
#if 1
  {
    MRI *mri_kernel, *mri_smooth, *mri_down ;
    float sigma = 16.0f ;

    mri_down = MRIdownsample2(mri_bias, NULL) ;
    mri_kernel = MRIgaussian1d(sigma, 100) ;
    mri_smooth = MRIconvolveGaussian(mri_down, NULL, mri_kernel) ;
    MRIfree(&mri_bias) ;
    MRIfree(&mri_kernel) ;
    mri_bias = MRIupsample2(mri_smooth, NULL) ;
    sigma = 2.0f ;
    MRIfree(&mri_down) ;
    MRIfree(&mri_smooth) ;
    mri_kernel = MRIgaussian1d(sigma, 100) ;
    mri_smooth = MRIconvolveGaussian(mri_bias, NULL, mri_kernel) ;
    MRIfree(&mri_bias) ;
    mri_bias = mri_smooth ;
    MRIfree(&mri_kernel) ;
  }
#else
  MRIsoapBubble(mri_bias, mri_ctrl, mri_bias, 10) ;
#endif
  /*  MRIwrite(mri_bias, "smooth_bias.mgz") ;*/


  width = mri_in->width ;
  height = mri_in->height ;
  depth = mri_in->depth ;
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        bias = (float)MRISvox(mri_bias, x, y, z)/NO_BIAS ;
        if (bias < 0)
          DiagBreak() ;
        for (input = 0 ; input < gca->ninputs ; input++)
        {
          MRIsampleVolumeFrame(mri_in, x, y, z, input, &val) ;
          val *= bias ;   /* corrected value */
          switch (mri_in->type)
          {
          case MRI_UCHAR:
            if (val < 0)
              val = 0 ;
            else if (val > 255)
              val = 255 ;
            MRIseq_vox(mri_dst, x, y, z, input) =
              (BUFTYPE)nint(val) ;
            break ;
          case MRI_SHORT:
            MRISseq_vox(mri_dst, x, y, z, input) =
              (short)nint(val) ;
            break ;
          case MRI_FLOAT:
            MRIFseq_vox(mri_dst, x, y, z, input) = val ;
            break ;
          default:
            ErrorReturn(NULL,
                        (ERROR_UNSUPPORTED,
                         "GCAnormalizeSamples: "
                         "unsupported input type %d",
                         mri_in->type));
            break ;
          }
        }
      }
    }
  }

  MRIfree(&mri_bias) ;
  MRIfree(&mri_ctrl) ;
  return(mri_dst) ;
}


void GCAnormalizeSamplesOneChannel(MRI *mri_in, GCA *gca,
                                   GCA_SAMPLE *gcas, int nsamples,
                                   TRANSFORM *transform,
                                   char *ctl_point_fname, int input_index)
/* This function is added by xhan, trying to normalize a single channel */
{

  MRI    *mri_dst, *mri_ctrl, *mri_bias ;
  int    xv, yv, zv, n, x, y, z, width, height, depth, xn, yn, zn, num, total;
  float   bias ;
  double  mean, sigma ;
  Real    val ;

  width = mri_in->width ;
  height = mri_in->height ;
  depth = mri_in->depth ;
  mri_dst = mri_in;
  mri_ctrl = MRIalloc(width, height, depth, MRI_UCHAR) ;
  MRIcopyHeader(mri_in, mri_ctrl);
  mri_bias = MRIalloc(mri_in->width,mri_in->height,mri_in->depth,MRI_SHORT);
  if (!mri_bias)
    ErrorExit(ERROR_NOMEMORY,
              "GCAnormalizeSamples: could not allocate "
              "(%d,%d,%d,2) bias image",
              mri_in->width,mri_in->height,mri_in->depth) ;
  MRIcopyHeader(mri_in, mri_bias);

#define MAX_BIAS 1250
#define NO_BIAS  1000
#define MIN_BIAS  750

  if (ctl_point_fname)
  {
    MRI3dUseFileControlPoints(mri_ctrl, ctl_point_fname) ;
    MRInormAddFileControlPoints(mri_ctrl, CONTROL_MARKED) ;
  }

  /* add control points from file */
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        MRISvox(mri_bias, x,y,z) = NO_BIAS ;  /* by default */
        if (MRIvox(mri_ctrl, x, y, z) != CONTROL_MARKED)
          /* not read from file */
          continue ;

        if (nsamples == 0)   /* only using file control points */
        {
          MRIsampleVolumeFrame(mri_in, x, y, z, input_index, &val) ;
          bias = NO_BIAS*DEFAULT_DESIRED_WHITE_MATTER_VALUE / val ;
          MRISvox(mri_bias, x, y, z) = (short)nint(bias) ;
        }
        else    /* find atlas point this maps to */
        {
          int       n, max_n ;
          GC1D      *gc ;
          GCA_NODE  *gcan ;
          GCA_PRIOR *gcap ;
          double    max_p ;

          if (!GCAsourceVoxelToNode(gca, mri_dst, transform,
                                    x, y, z, &xn, &yn, &zn))
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            gcap = getGCAP(gca, mri_dst, transform, x, y, z) ;
            if (gcap==NULL)
              continue;
            max_p = 0 ;
            for (max_n = -1, n = 0 ; n < gcan->nlabels ; n++)
            {
              if ((0 == IS_WM(gcan->labels[n])) &&
                  (0 == IS_CEREBELLAR_WM(gcan->labels[n])) &&
                  (gcan->labels[n] != Brain_Stem))
                continue ;
              gc = &gcan->gcs[n] ;
              if (getPrior(gcap, gcan->labels[n]) >= max_p)
              {
                max_p = getPrior(gcap, gcan->labels[n]) ;
                max_n = n ;
              }
            }
            if (max_n < 0)
              /* couldn't find any valid label at this location */
              continue ;
            gc = &gcan->gcs[max_n] ;


            MRIsampleVolumeFrame(mri_in, x, y, z,
                                 input_index, &val) ;
            if (FZERO(val))
              val = 1 ;
            bias =
              (float)NO_BIAS*((float)gc->means[input_index]/val) ;

            if (bias < 100 || bias > 5000)
              DiagBreak() ;
            if (bias < MIN_BIAS)
              bias = MIN_BIAS ;
            if (bias > MAX_BIAS)
              bias = MAX_BIAS ;

            MRISvox(mri_bias, x, y, z) = (short)nint(bias) ;
          }
          /////////////////////////////////////////////////////
        }
      }
    }
  }

  TransformInvert(transform, mri_in) ;
  for (n = 0 ; n < nsamples ; n++)
  {
    if (gcas[n].xp == Ggca_x && gcas[n].yp == Ggca_y && gcas[n].zp == Ggca_z)
      DiagBreak() ;

    if (!GCApriorToSourceVoxel(gca, mri_dst, transform,
                               gcas[n].xp, gcas[n].yp, gcas[n].zp,
                               &xv, &yv, &zv))
    {
      if (xv == 181 && yv == 146 && zv == 128)
        DiagBreak() ;
      if (xv == Ggca_x && yv == Ggca_y && zv == Ggca_z)
        DiagBreak() ;
      if (gcas[n].label == 29 || gcas[n].label == 61)
      {
        gcas[n].label = 0 ;
        DiagBreak() ;
      }
      if (gcas[n].label > 0)
      {
        MRIvox(mri_ctrl, xv, yv, zv) = CONTROL_MARKED ;


        MRIsampleVolumeFrame(mri_in, xv, yv, zv, input_index, &val) ;
        if (FZERO(val))
          val = 1 ;
        bias = (float)NO_BIAS*((float)gcas[n].means[input_index]/val) ;

        if (bias < 100 || bias > 5000)
          DiagBreak() ;
#if 0
        if (bias < MIN_BIAS)
          bias = MIN_BIAS ;
        if (bias > MAX_BIAS)
          bias = MAX_BIAS ;
#endif

        MRISvox(mri_bias, xv, yv, zv) = (short)nint(bias) ;
      }
      else
        MRIvox(mri_ctrl, xv, yv, zv) = CONTROL_NONE ;
    }
  }

  /* now check for and remove outliers */
  mean = sigma = 0.0 ;
  for (num = z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        if (MRIvox(mri_ctrl, x, y, z) == CONTROL_MARKED)
        {
          num++ ;
          bias = (double)MRISvox(mri_bias, x, y, z) ;
          mean += bias ;
          sigma += (bias*bias) ;
        }
      }
    }
  }

  if (num > 0)
  {
    mean /= (double)num ;
    sigma  = sqrt(sigma / (double)num - mean*mean) ;
    printf("bias field = %2.3f +- %2.3f\n", mean/NO_BIAS, sigma/NO_BIAS) ;
  }

  /* now check for and remove outliers */
  for (total = num = z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        if (MRIvox(mri_ctrl, x, y, z) == CONTROL_MARKED)
        {
          bias = (double)MRISvox(mri_bias, x, y, z) ;
          total++ ;
          if (fabs(bias-mean) > 4*sigma)
          {
            MRIvox(mri_ctrl, x, y, z) = CONTROL_NONE ;
            num++ ;
            MRISvox(mri_bias, x, y, z) = NO_BIAS ;
          }
        }
      }
    }
  }

  printf("%d of %d control points discarded\n", num, total) ;

  MRIbuildVoronoiDiagram(mri_bias, mri_ctrl, mri_bias) ;
  /*  MRIwrite(mri_bias, "bias.mgz") ;*/
#if 1
  {
    MRI *mri_kernel, *mri_smooth, *mri_down ;
    float sigma = 16.0f ;

    mri_down = MRIdownsample2(mri_bias, NULL) ;
    mri_kernel = MRIgaussian1d(sigma, 100) ;
    mri_smooth = MRIconvolveGaussian(mri_down, NULL, mri_kernel) ;
    MRIfree(&mri_bias) ;
    MRIfree(&mri_kernel) ;
    mri_bias = MRIupsample2(mri_smooth, NULL) ;
    sigma = 2.0f ;
    MRIfree(&mri_down) ;
    MRIfree(&mri_smooth) ;
    mri_kernel = MRIgaussian1d(sigma, 100) ;
    mri_smooth = MRIconvolveGaussian(mri_bias, NULL, mri_kernel) ;
    MRIfree(&mri_bias) ;
    mri_bias = mri_smooth ;
    MRIfree(&mri_kernel) ;
  }
#else
  MRIsoapBubble(mri_bias, mri_ctrl, mri_bias, 10) ;
#endif
  /*  MRIwrite(mri_bias, "smooth_bias.mgz") ;*/


  width = mri_in->width ;
  height = mri_in->height ;
  depth = mri_in->depth ;
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        bias = (float)MRISvox(mri_bias, x, y, z)/NO_BIAS ;
        if (bias < 0)
          DiagBreak() ;

        MRIsampleVolumeFrame(mri_in, x, y, z, input_index, &val) ;
        val *= bias ;   /* corrected value */
        switch (mri_in->type)
        {
        case MRI_UCHAR:
          if (val < 0)
            val = 0 ;
          else if (val > 255)
            val = 255 ;
          MRIseq_vox(mri_dst, x, y, z, input_index) =
            (BUFTYPE)nint(val) ;
          break ;
        case MRI_SHORT:
          MRISseq_vox(mri_dst, x, y, z, input_index) =
            (short)nint(val) ;
          break ;
        case MRI_FLOAT:
          MRIFseq_vox(mri_dst, x, y, z, input_index) = val ;
          break ;
        default:
          ErrorPrintf(ERROR_UNSUPPORTED,
                      "GCAnormalizeSamples: unsupported input type %d",
                      mri_in->type);
          break ;
        }

      }
    }
  }

  MRIfree(&mri_bias) ;
  MRIfree(&mri_ctrl) ;
  return;
}

MRI *
GCAnormalizeSamplesAllChannels(MRI *mri_in,
                               GCA *gca,
                               GCA_SAMPLE *gcas,
                               int nsamples,
                               TRANSFORM *transform,
                               char *ctl_point_fname)
{
  MRI    *mri_dst;
  int input;

  mri_dst = MRIcopy(mri_in, NULL);

  for (input = 0; input < gca->ninputs; input++)
  {
    GCAnormalizeSamplesOneChannel(mri_dst, gca, gcas, nsamples,
                                  transform, ctl_point_fname, input);
  }

  return (mri_dst);

}

MRI *
GCAnormalizeSamplesT1PD(MRI *mri_in, GCA *gca,
                        GCA_SAMPLE *gcas, int nsamples,
                        TRANSFORM *transform, char *ctl_point_fname)
{
  MRI    *mri_dst, *mri_ctrl, *mri_bias ;
  int    xv, yv, zv, n, x, y, z, width, height, depth, \
  out_val, xn, yn, zn, num, total ;
  Real   val ;
  float   bias ;
  double  mean, sigma ;

  mri_dst = MRIclone(mri_in, NULL) ;
  mri_ctrl = MRIalloc(mri_in->width,mri_in->height,mri_in->depth,MRI_UCHAR);
  MRIcopyHeader(mri_in, mri_ctrl);
  mri_bias = MRIalloc(mri_in->width,mri_in->height,mri_in->depth,MRI_SHORT);
  if (!mri_bias)
    ErrorExit(ERROR_NOMEMORY,
              "GCAnormalize: could not allocate (%d,%d,%d,2) bias image",
              mri_in->width,mri_in->height,mri_in->depth) ;
  MRIcopyHeader(mri_in, mri_bias);

#define MAX_BIAS 1250
#define NO_BIAS  1000
#define MIN_BIAS  750

  if (ctl_point_fname)
  {
    MRI3dUseFileControlPoints(mri_ctrl, ctl_point_fname) ;
    MRInormAddFileControlPoints(mri_ctrl, CONTROL_MARKED) ;
  }
  width = mri_in->width ;
  height = mri_in->height ;
  depth = mri_in->depth ;

  /* add control points from file */
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        MRISvox(mri_bias, x,y,z) = NO_BIAS ;  /* by default */
        if (MRIvox(mri_ctrl, x, y, z) == CONTROL_MARKED)
          /* read from file */
        {
          int       n, max_n ;
          GC1D      *gc ;
          GCA_NODE  *gcan ;
          GCA_PRIOR *gcap ;
          double    max_p ;

          if (!GCAsourceVoxelToNode(gca, mri_dst,
                                    transform,
                                    x, y, z, &xn, &yn, &zn))
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            gcap = getGCAP(gca, mri_dst, transform, x, y, z) ;
            if (gcap==NULL)
              continue;
            max_p = 0 ;
            for (max_n = -1, n = 0 ; n < gcan->nlabels ; n++)
            {
              if ((0 == IS_WM(gcan->labels[n])) &&
                  (0 == IS_CEREBELLAR_WM(gcan->labels[n])))
                continue ;
              gc = &gcan->gcs[n] ;
              if (getPrior(gcap, gcan->labels[n]) >= max_p)
              {
                max_p = getPrior(gcap, gcan->labels[n]) ;
                max_n = n ;
              }
            }
            if (max_n < 0)
              /* couldn't find any valid label at this location */
              continue ;
            gc = &gcan->gcs[max_n] ;

            MRIsampleVolumeFrameType(mri_in, x, y, z, 1,
                                     SAMPLE_NEAREST, &val) ;
            if (FZERO(val))
              val = 1 ;
            bias = (float)NO_BIAS*((float)gc->means[1]/val) ;
            if (bias < 100 || bias > 5000)
              DiagBreak() ;
            if (bias < MIN_BIAS)
              bias = MIN_BIAS ;
            if (bias > MAX_BIAS)
              bias = MAX_BIAS ;

            MRISvox(mri_bias, x, y, z) = (short)nint(bias) ;
          }
          ////////////////////////////////////////////////
        }
      }
    }
  }


  TransformInvert(transform, mri_in) ;
  for (n = 0 ; n < nsamples ; n++)
  {
    if (gcas[n].xp == Ggca_x && gcas[n].yp == Ggca_y && gcas[n].zp == Ggca_z)
      DiagBreak() ;

    if (!GCApriorToSourceVoxel(gca, mri_dst, transform,
                               gcas[n].xp, gcas[n].yp, gcas[n].zp,
                               &xv, &yv, &zv))
    {

      if (xv == 181 && yv == 146 && zv == 128)
        DiagBreak() ;
      if (xv == Ggca_x && yv == Ggca_y && zv == Ggca_z)
        DiagBreak() ;
      if (gcas[n].label == 29 || gcas[n].label == 61)
      {
        gcas[n].label = 0 ;
        DiagBreak() ;
      }
      if (gcas[n].label > 0)
      {
        MRIvox(mri_ctrl, xv, yv, zv) = CONTROL_MARKED ;
        MRIsampleVolumeFrameType(mri_in, xv, yv, zv, 1,
                                 SAMPLE_NEAREST, &val) ;
        if (FZERO(val))
          val = 1 ;
        bias = (float)NO_BIAS*((float)gcas[n].means[1]/val) ;
        if (bias < 100 || bias > 5000)
          DiagBreak() ;
#if 0
        if (bias < MIN_BIAS)
          bias = MIN_BIAS ;
        if (bias > MAX_BIAS)
          bias = MAX_BIAS ;
#endif

        MRISvox(mri_bias, xv, yv, zv) = (short)nint(bias) ;
      }
      else
        MRIvox(mri_ctrl, xv, yv, zv) = CONTROL_NONE ;
    }
  }

  /* now check for and remove outliers */
  mean = sigma = 0.0 ;
  for (num = z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        if (MRIvox(mri_ctrl, x, y, z) == CONTROL_MARKED)
        {
          num++ ;
          bias = (double)MRISvox(mri_bias, x, y, z) ;
          mean += bias ;
          sigma += (bias*bias) ;
        }
      }
    }
  }

  if (num > 0)
  {
    mean /= (double)num ;
    sigma  = sqrt(sigma / (double)num - mean*mean) ;
    printf("bias field = %2.3f +- %2.3f\n", mean/NO_BIAS, sigma/NO_BIAS) ;
  }

  /* now check for and remove outliers */
  for (total = num = z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        if (MRIvox(mri_ctrl, x, y, z) == CONTROL_MARKED)
        {
          bias = (double)MRISvox(mri_bias, x, y, z) ;
          total++ ;
          if (fabs(bias-mean) > 4*sigma)
          {
            MRIvox(mri_ctrl, x, y, z) = CONTROL_NONE ;
            num++ ;
            MRISvox(mri_bias, x, y, z) = NO_BIAS ;
          }
        }
      }
    }
  }

  printf("%d of %d control points discarded\n", num, total) ;

  MRIbuildVoronoiDiagram(mri_bias, mri_ctrl, mri_bias) ;
  /*  MRIwrite(mri_bias, "bias.mgz") ;*/
#if 0
  {
    MRI *mri_kernel, *mri_smooth ;

    mri_kernel = MRIgaussian1d(2.0f, 100) ;
    mri_smooth = MRIconvolveGaussian(mri_bias, NULL, mri_kernel) ;
    MRIfree(&mri_bias) ;
    mri_bias = mri_smooth ;
    MRIfree(&mri_kernel) ;
  }
#else
  MRIsoapBubble(mri_bias, mri_ctrl, mri_bias, 10) ;
#endif
  /*  MRIwrite(mri_bias, "smooth_bias.mgz") ;*/


  width = mri_in->width ;
  height = mri_in->height ;
  depth = mri_in->depth ;
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        bias = (float)MRISvox(mri_bias, x, y, z)/NO_BIAS ;
        if (bias < 0)
          DiagBreak() ;
        MRIsampleVolumeFrameType(mri_in, x, y, z, 1,
                                 SAMPLE_NEAREST, &val) ;
        out_val = nint((float)val*bias) ;
        MRISseq_vox(mri_dst, x, y, z, 1) = (short)nint(out_val) ;
      }
    }
  }

  MRIcopyFrame(mri_in, mri_dst, 0, 0) ;  /* copy over T1 frame */
  MRIfree(&mri_bias) ;
  MRIfree(&mri_ctrl) ;
  return(mri_dst) ;
}

float
GCAlabelProbability(MRI *mri_src, GCA *gca, TRANSFORM *transform,
                    float x, float y, float z, int label)
{
  int      xn, yn, zn ;
  GCA_NODE *gcan ;
  GC1D     *gc ;
  float    plabel, vals[MAX_GCA_INPUTS] ;

  if (x == Gx && y == Gy && z == Gz)
    DiagBreak() ;

  if (!GCAsourceVoxelToNode(gca, mri_src, transform, x, y, z, &xn, &yn, &zn))
  {
    gcan = &gca->nodes[xn][yn][zn] ;

    load_vals(mri_src, x, y, z, vals, gca->ninputs) ;
    gc = GCAfindGC(gca, xn, yn, zn, label) ;
    if (gc == NULL || gc->ntraining == 0)
      gc = findClosestValidGC(gca, xn, yn, zn, label, 0) ;

    if (gc == NULL)
      plabel = 0.0 ;
    else
      plabel = GCAcomputeConditionalDensity(gc, vals, gca->ninputs, label) ;
  }
  else
    plabel = 0.0;
  return(plabel) ;
}

static GCA_NODE *
findSourceGCAN(GCA *gca, MRI *mri_src, TRANSFORM *transform,
               int x, int y, int z)
{
  int      xn, yn, zn ;
  GCA_NODE *gcan=NULL;

  if (!GCAsourceVoxelToNode(gca, mri_src, transform, x, y, z, &xn, &yn, &zn))
  {
    gcan = &gca->nodes[xn][yn][zn] ;
  }
  return(gcan) ;
}
#if 0
static int
getLabelMean(GCA_NODE *gcan, int label, float *pvar, float *means, int ninputs)
{
  int   n, r ;
  float mean = -1.0f ;

  if (*pvar)
    *pvar = 0.0f ;
  for (n = 0 ; n < gcan->nlabels ; n++)
  {
    if (gcan->labels[n] == label)
    {
      for (r = 0 ; r < ninputs ; r++)
        mean = gcan->gcs[n].means[r] ;
      if (pvar)
        *pvar = covariance_determinant(&gcan->gcs[n], ninputs) ;
      ;
      break ;
    }
  }
  return(NO_ERROR) ;
}
#endif
MRI   *
GCAmaxLikelihoodBorders(GCA *gca, MRI *mri_inputs, MRI *mri_src,
                        MRI *mri_dst, TRANSFORM *transform,
                        int max_iter, float min_ratio)
{
  int      nchanged, x, y, z, width, height, depth, label, total_changed, i ;
  MRI      *mri_tmp ;

  if (mri_src != mri_dst)
    mri_dst = MRIcopy(mri_src, mri_dst) ;

  mri_tmp = MRIcopy(mri_dst, NULL) ;

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

  for (total_changed = i = 0 ; i < max_iter ; i++)
  {
    nchanged = 0 ;
    for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (x = 0 ; x < width ; x++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;
          if (x == 99 && y == 129 && z == 127)
            DiagBreak() ;  /* gray should be wm */
          if (x == 98 && y == 124 && z == 127)
            DiagBreak() ;  /* wm should be hippo */

          if (borderVoxel(mri_dst,x,y,z))
          {
            label = GCAmaxLikelihoodBorderLabel(gca,
                                                mri_inputs,
                                                mri_dst,transform,
                                                x, y, z, min_ratio) ;
            if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
                (label == Ggca_label
                 || nint(MRIgetVoxVal(mri_tmp,x,y,z,0)) == Ggca_label ||
                 Ggca_label < 0))
            {
              DiagBreak() ;

              if (label != nint(MRIgetVoxVal(mri_dst, x, y, z,0)))
                printf(
                  "MLE (%d, %d, %d): old label %s (%d), "
                  "new label %s (%d)\n",
                  x, y, z,
                  cma_label_to_name(nint(MRIgetVoxVal(mri_tmp,x,y,z,0))),
                  nint(MRIgetVoxVal(mri_tmp,x,y,z,0)),
                  cma_label_to_name(label),
                  label) ;
            }
            if (label != nint(MRIgetVoxVal(mri_dst, x, y, z,0)))
            {
              nchanged++ ;
              MRIsetVoxVal(mri_tmp, x, y, z, 0, label) ;
            }
          }
        }
      }
    }
    MRIcopy(mri_tmp, mri_dst) ;
    total_changed += nchanged ;
    if (!nchanged)
      break ;
  }

  MRIfree(&mri_tmp) ;
  printf("%d border labels changed to MLE ...\n", total_changed) ;
  return(mri_dst) ;
}
static int
borderVoxel(MRI *mri, int x, int y, int z)
{
  int   xi, yi, zi, xk, yk, zk, label ;

  label = nint(MRIgetVoxVal(mri, x, y, z,0)) ;

  for (xk = -1 ; xk <= 1 ; xk++)
  {
    xi = mri->xi[x+xk] ;
    for (yk = -1 ; yk <= 1 ; yk++)
    {
      for (zk = -1 ; zk <= 1 ; zk++)
      {
        if (abs(xk)+abs(yk)+abs(zk) != 1)
          continue ;
        yi = mri->yi[y+yk] ;
        zi = mri->zi[z+zk] ;
        if (nint(MRIgetVoxVal(mri, xi, yi, zi, 0)) != label)
          return(1) ;
      }
    }
  }
  return(0) ;
}

static int
GCAmaxLikelihoodBorderLabel(GCA *gca, MRI *mri_inputs, MRI *mri_labels,
                            TRANSFORM *transform,
                            int x, int y, int z, float min_ratio)
{
  float    p, max_p, vals[MAX_GCA_INPUTS] ;
  int      label, i, xi, yi, zi, best_label, orig_label, n ;
  GCA_NODE *gcan ;
  GC1D     *gc ;

  if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
    DiagBreak() ;

  load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
  orig_label = best_label = nint(MRIgetVoxVal(mri_labels, x, y, z, 0)) ;

  // current GCA_NODE at this point
  gcan = findSourceGCAN(gca, mri_inputs, transform, x, y, z) ;
  if (gcan == NULL)
    return(orig_label) ;

  // current label
  // look for classifier for this label and get the p value
  gc = NULL ;
  for (n = 0 ; n < gcan->nlabels ; n++)
    if (gcan->labels[n] == best_label)
    {
      gc  = &gcan->gcs[n] ;
      break ;
    }

  if (gc == NULL)
  {
    ErrorPrintf(ERROR_BADPARM,
                "GCAmaxLikelihoodBorderLabel(%d, %d, %d): "
                "couldn't find gc for label %d",
                x, y, z, best_label) ;
    max_p = 0.0 ;
  }
  else
    max_p = GCAcomputeConditionalDensity(gc, vals, gca->ninputs, best_label) ;

  // look around neighbors ///////////////////////////////
  for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
  {
    xi = mri_inputs->xi[x+xnbr_offset[i]] ;
    yi = mri_inputs->yi[y+ynbr_offset[i]] ;
    zi = mri_inputs->zi[z+znbr_offset[i]] ;
    gcan = findSourceGCAN(gca, mri_inputs, transform, xi, yi, zi) ;
    if (gcan == NULL)
      continue ;
    // get the neighbor label
    label = nint(MRIgetVoxVal(mri_labels, xi, yi, zi, 0)) ;
    gc = NULL ;
    for (n = 0 ; n < gcan->nlabels ; n++)
      if (gcan->labels[n] == label)
      {
        // get the classifier for this label at this location
        gc  = &gcan->gcs[n] ;
        break ;
      }
    if (gc == NULL)
      continue ;  /* label can't occur here */

    // set the label to this neighbor value
    MRIsetVoxVal(mri_labels, x, y, z, 0, label) ;
    // check if possible
    if (gcaGibbsImpossibleConfiguration(gca, mri_labels,
                                        x, y, z, transform))
    {
      MRIsetVoxVal(mri_labels, x, y, z, 0, orig_label) ; // added by xh
      continue ; // shouldn't put the original label back ???? -xh
    }
    // restore the old value
    MRIsetVoxVal(mri_labels, x, y, z, 0, orig_label) ;

    // calculate p for this neighbor label
    p = GCAcomputeConditionalDensity(gc, vals, gca->ninputs, label) ;
    //
    if (((best_label == orig_label && p > min_ratio*max_p) ||
         // starting loop
         (best_label != orig_label && p >= max_p)) &&
        // later in the loop
        GCAisPossible(gca, mri_labels, label, transform, x, y, z, 0))
    {
      max_p = p ;
      best_label = label ;
    }
  }

  /* test to make sure that it is not an impossible Gibbs configuration */
  if (best_label != nint(MRIgetVoxVal(mri_labels, x, y, z, 0)))
  {
    label = nint(MRIgetVoxVal(mri_labels, x, y, z, 0)) ;
    MRIsetVoxVal(mri_labels, x, y, z, 0, best_label) ; /* test potential new label */
    if (gcaGibbsImpossibleConfiguration(gca, mri_labels, x, y,z, transform))
      best_label = label ;  /* revert back to old label */
    MRIsetVoxVal(mri_labels, x, y, z, 0, label) ;
    /* caller will change it if needed */
  }
  return(best_label) ;
}


int
GCAcomputeLabelStats(GCA *gca, int target_label, float *pvar, float *means)
{
  int      x, y, z, n, r ;
  double   var, dof, total_dof ;
  GC1D     *gc ;
  GCA_NODE *gcan ;
  float fval;

  var = total_dof = 0.0 ;
  memset(means, 0, gca->ninputs*sizeof(float)) ;
  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;

        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (gcan->labels[n] == target_label)
          {
            gc = &gcan->gcs[n] ;
            fval = get_node_prior(gca, target_label, x,y,z);
            if (fval != 0)
            {
              dof =
                get_node_prior(gca, target_label, x, y, z) \
                * gcan->total_training ;
              for (r = 0 ; r < gca->ninputs ; r++)
                means[r] += dof*gc->means[r] ;
              var += dof*covariance_determinant(gc, gca->ninputs) ;
              total_dof += dof ;
            }
          }
        }
      }
    }
  }

  if (total_dof > 0.0)
  {
    for (r = 0 ; r < gca->ninputs ; r++)
      means[r] /= total_dof ;
    var /= total_dof ;
  }
  if (pvar)
    *pvar = var ;
  return(NO_ERROR) ;
}
int
GCAhistogramTissueStatistics(GCA *gca, MRI *mri_T1,MRI *mri_PD,
                             MRI *mri_labeled,
                             TRANSFORM *transform, char *fname)
{
  int              x, y, z, n, label, biggest_label, T1, PD, xp, yp, zp ;
  GCA_NODE         *gcan ;
  GCA_TISSUE_PARMS *gca_tp ;
  VECTOR           *v_parc, *v_T1 ;
  static VECTOR *v_ras_cor = NULL, *v_ras_flash ;
  FILE             *fp ;
  float            xf, yf, zf ;

  fp = fopen(fname, "w") ;
  if (!fp)
    ErrorExit(ERROR_NOFILE, "GCAhistogramTissueStatistics: could not open %s",
              fname) ;

  v_parc = VectorAlloc(4, MATRIX_REAL) ;
  v_T1 = VectorAlloc(4, MATRIX_REAL) ;
  *MATRIX_RELT(v_parc, 4, 1) = 1.0 ;
  *MATRIX_RELT(v_T1, 4, 1) = 1.0 ;

  /* first build a list of all labels that exist */
  for (biggest_label = x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_height ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          gca->tissue_parms[(int)gcan->labels[n]].label
          = gcan->labels[n] ;
          if (gcan->labels[n] > biggest_label)
            biggest_label = gcan->labels[n] ;
        }
      }
    }
  }

  for (label = 0 ; label <= biggest_label ; label++)
  {
    if (gca->tissue_parms[label].label <= 0)
      continue ;
    gca_tp = &gca->tissue_parms[label] ;

    for (z = 0 ; z < mri_T1->depth ; z++)
    {
      V3_Z(v_parc) = z ;
      for (y = 0 ; y < mri_T1->height ; y++)
      {
        V3_Y(v_parc) = y ;
        for (x = 0 ; x < mri_T1->width ; x++)
        {
          if (nint(MRIgetVoxVal(mri_labeled, x, y, z, 0)) != label)
            continue ;
          if (borderVoxel(mri_labeled, x, y, z))
            continue ;
          if (transform)
          {
            MATRIX *m_tmp ;
            V3_X(v_parc) = x ;

            TransformSample(transform,
                            x*mri_T1->xsize,
                            y*mri_T1->ysize,
                            z*mri_T1->zsize,
                            &xf, &yf, &zf) ;
            xp = nint(xf) ;
            yp = nint(zf) ;
            zp = nint(zf) ;
            V3_X(v_T1) = xp ;
            V3_Y(v_T1) = yp ;
            V3_Z(v_T1) = zp ;

            m_tmp = MRIgetVoxelToRasXform(mri_labeled) ;
            v_ras_cor = MatrixMultiply(m_tmp, v_parc, v_ras_cor);
            MatrixFree(&m_tmp) ;
            m_tmp = MRIgetVoxelToRasXform(mri_T1) ;
            v_ras_flash = MatrixMultiply(m_tmp, v_T1, v_ras_flash);
            MatrixFree(&m_tmp) ;
            if (!x && !y && !z && 0)
            {
              MatrixPrint(stdout, v_ras_cor) ;
              MatrixPrint(stdout, v_ras_flash) ;
            }

            if ((xp < 0 || xp >= mri_T1->width) ||
                (yp < 0 || yp >= mri_T1->height) ||
                (zp < 0 || zp >= mri_T1->depth))
              continue ;
          }
          else
          {
            xp = x ;
            yp = y ;
            zp = z ;
          }

          T1 = MRISvox(mri_T1, xp, yp, zp) ;
          PD = MRISvox(mri_PD, xp, yp, zp) ;
          fprintf(fp, "%d %d %d\n", label, T1, PD) ;
          gca_tp->total_training++ ;
          gca_tp->T1_mean += T1 ;
          gca_tp->T1_var += T1*T1 ;
          gca_tp->PD_mean += PD ;
          gca_tp->PD_var += PD*PD ;
        }
      }
    }
  }

  fclose(fp) ;
  return(NO_ERROR) ;
}

int
GCAnormalizeTissueStatistics(GCA *gca)
{
  int              n ;
  double           nsamples ;
  GCA_TISSUE_PARMS *gca_tp ;

  for (n = 0 ; n < MAX_GCA_LABELS ; n++)
  {
    gca_tp = &gca->tissue_parms[n] ;
    if (gca_tp->total_training <= 0)
      continue ;
    nsamples = gca_tp->total_training ;
    gca_tp->T1_mean /= nsamples ;
    gca_tp->PD_mean /= nsamples ;
    gca_tp->T2_mean /= nsamples ;
    gca_tp->T1_var =
      gca_tp->T1_var / nsamples - gca_tp->T1_mean*gca_tp->T1_mean;
    gca_tp->PD_var =
      gca_tp->PD_var / nsamples - gca_tp->PD_mean*gca_tp->PD_mean;
    printf("%s: T1=%4d +- %4d, PD=%4d +- %4d \n",
           cma_label_to_name(n),
           nint(gca_tp->T1_mean), nint(sqrt(gca_tp->T1_var)),
           nint(gca_tp->PD_mean), nint(sqrt(gca_tp->PD_var))) ;
  }

  return(NO_ERROR) ;
}


char *
cma_label_to_name(int label)
{
  static char name[STRLEN] ;

  if (label == Unknown)
    return("Unknown") ;
  if (label == Left_Cerebral_Exterior)
    return("Left_Cerebral_Exterior") ;
  if (label == Left_Cerebral_White_Matter)
    return("Left_Cerebral_White_Matter") ;
  if (label == Left_Cerebral_Cortex)
    return("Left_Cerebral_Cortex") ;
  if (label == Left_Lateral_Ventricle)
    return("Left_Lateral_Ventricle") ;
  if (label == Left_Inf_Lat_Vent)
    return("Left_Inf_Lat_Vent") ;
  if (label == Left_Cerebellum_Exterior)
    return("Left_Cerebellum_Exterior") ;
  if (label == Left_Cerebellum_White_Matter)
    return("Left_Cerebellum_White_Matter") ;
  if (label == Left_Cerebellum_Cortex)
    return("Left_Cerebellum_Cortex") ;
  if (label == Left_Thalamus)
    return("Left_Thalamus") ;
  if (label == Left_Thalamus_Proper)
    return("Left_Thalamus_Proper") ;
  if (label == Left_Caudate)
    return("Left_Caudate") ;
  if (label == Left_Putamen)
    return("Left_Putamen") ;
  if (label == Left_Pallidum)
    return("Left_Pallidum") ;
  if (label == Third_Ventricle)
    return("Third_Ventricle") ;
  if (label == Fourth_Ventricle)
    return("Fourth_Ventricle") ;
  if (label == Brain_Stem)
    return("Brain_Stem") ;
  if (label == Left_Hippocampus)
    return("Left_Hippocampus") ;
  if (label == Left_Amygdala)
    return("Left_Amygdala") ;
  if (label == Left_Amygdala_Anterior)
    return("Left_Amygdala_Anterior") ;
  if (label == Left_Insula)
    return("Left_Insula") ;
  if (label == Left_Operculum)
    return("Left_Operculum") ;
  if (label == Line_1)
    return("Line_1") ;
  if (label == Line_2)
    return("Line_2") ;
  if (label == Line_3)
    return("Line_3") ;
  if (label == CSF)
    return("CSF") ;
  if (label == Left_Lesion)
    return("Left_Lesion") ;
  if (label == Left_Accumbens_area)
    return("Left_Accumbens_area") ;
  if (label == Left_Substancia_Nigra)
    return("Left_Substancia_Nigra") ;
  if (label == Left_VentralDC)
    return("Left_VentralDC") ;
  if (label == Left_undetermined)
    return("Left_undetermined") ;
  if (label == Left_vessel)
    return("Left_vessel") ;
  if (label == Left_choroid_plexus)
    return("Left_choroid_plexus") ;
  if (label == Left_F3orb)
    return("Left_F3orb") ;
  if (label == Left_lOg)
    return("Left_lOg") ;
  if (label == Left_aOg)
    return("Left_aOg") ;
  if (label == Left_mOg)
    return("Left_mOg") ;
  if (label == Left_pOg)
    return("Left_pOg") ;
  if (label == Left_Stellate)
    return("Left_Stellate") ;
  if (label == Left_Porg)
    return("Left_Porg") ;
  if (label == Left_Aorg)
    return("Left_Aorg") ;
  if (label == Right_Cerebral_Exterior)
    return("Right_Cerebral_Exterior") ;
  if (label == Right_Cerebral_White_Matter)
    return("Right_Cerebral_White_Matter") ;
  if (label == Right_Cerebral_Cortex)
    return("Right_Cerebral_Cortex") ;
  if (label == Right_Lateral_Ventricle)
    return("Right_Lateral_Ventricle") ;
  if (label == Right_Inf_Lat_Vent)
    return("Right_Inf_Lat_Vent") ;
  if (label == Right_Cerebellum_Exterior)
    return("Right_Cerebellum_Exterior") ;
  if (label == Right_Cerebellum_White_Matter)
    return("Right_Cerebellum_White_Matter") ;
  if (label == Right_Cerebellum_Cortex)
    return("Right_Cerebellum_Cortex") ;
  if (label == Right_Thalamus)
    return("Right_Thalamus") ;
  if (label == Right_Thalamus_Proper)
    return("Right_Thalamus_Proper") ;
  if (label == Right_Caudate)
    return("Right_Caudate") ;
  if (label == Right_Putamen)
    return("Right_Putamen") ;
  if (label == Right_Pallidum)
    return("Right_Pallidum") ;
  if (label == Right_Hippocampus)
    return("Right_Hippocampus") ;
  if (label == Right_Amygdala)
    return("Right_Amygdala") ;
  if (label == Right_Amygdala_Anterior)
    return("Right_Amygdala_Anterior") ;
  if (label == Right_Insula)
    return("Right_Insula") ;
  if (label == Right_Operculum)
    return("Right_Operculum") ;
  if (label == Right_Lesion)
    return("Right_Lesion") ;
  if (label == Right_Accumbens_area)
    return("Right_Accumbens_area") ;
  if (label == Right_Substancia_Nigra)
    return("Right_Substancia_Nigra") ;
  if (label == Right_VentralDC)
    return("Right_VentralDC") ;
  if (label == Right_undetermined)
    return("Right_undetermined") ;
  if (label == Right_vessel)
    return("Right_vessel") ;
  if (label == Right_choroid_plexus)
    return("Right_choroid_plexus") ;
  if (label == Right_F3orb)
    return("Right_F3orb") ;
  if (label == Right_lOg)
    return("Right_lOg") ;
  if (label == Right_aOg)
    return("Right_aOg") ;
  if (label == Right_mOg)
    return("Right_mOg") ;
  if (label == Right_pOg)
    return("Right_pOg") ;
  if (label == Right_Stellate)
    return("Right_Stellate") ;
  if (label == Right_Porg)
    return("Right_Porg") ;
  if (label == Right_Aorg)
    return("Right_Aorg") ;
  if (label == Bone)
    return("Bone") ;
  if (label == Fat)
    return("Fat") ;
  if (label == Bright_Unknown)
    return("Bright Unknown") ;
  if (label == Dark_Unknown)
    return("Dark Unknown") ;

  if (label == Left_Interior)
    return("Left_Interior") ;
  if (label == Right_Interior)
    return("Right_Interior") ;
  if (label == Left_Lateral_Ventricles)
    return("Left_Lateral_Ventricles") ;
  if (label == Right_Lateral_Ventricles)
    return("Right_Lateral_Ventricles") ;
  if (label == WM_hypointensities)
    return("WM_hypointensities") ;
  if (label == Left_WM_hypointensities)
    return("Left_WM_hypointensities") ;
  if (label == Right_WM_hypointensities)
    return("Right_WM_hypointensities") ;
  if (label == non_WM_hypointensities)
    return("non_WM_hypointensities") ;
  if (label == Left_non_WM_hypointensities)
    return("Left_non_WM_hypointensities") ;
  if (label == Right_non_WM_hypointensities)
    return("Right_non_WM_hypointensities") ;
  if (label == Fifth_Ventricle)
    return("Fifth_Ventricle") ;
  if (label == Optic_Chiasm)
    return("Optic_Chiasm") ;
  if (label == Cranium)
    return("Cranium") ;
  if (label == Dura)
    return("Dura") ;
  if (label == CSF_SA)
    return("CSF_SA") ;
  if (label == Ear)
    return("Ear") ;
  if (label == Muscle)
    return("Muscle") ;
  if (label == Epidermis)
    return("Epidermis") ;
  if (label == Conn_Tissue)
    return("Conn_Tissue") ;
  if (label == SC_FAT_MUSCLE)
    return("SC-Fat/Muscle") ;
  if (label == Fatty_Tissue)
    return("Fatty_Tissue") ;
  if (label == Spinal_Cord)
    return("Spinal_Cord") ;
  if (label == Soft_Tissue)
    return("Soft_Tissue") ;
  if (label == Nerve)
    return("Nerve") ;
  if (label == Bone)
    return("Bone") ;
  if (label == Air)
    return("Air") ;
  if (label == Orbit)
    return("Orbit") ;
  if (label == Tongue)
    return("Tongue") ;
  if (label == Nasal_Structures)
    return("Nasal_Structures") ;
  if (label == Globe)
    return("Globe") ;
  if (label == Teeth)
    return("Teeth") ;

  if (label == alveus)
    return("alveus") ;
  if (label == perforant_pathway    )
    return("perforant_pathway") ;
  if (label == parasubiculum    )
    return("parasubiculum") ;
  if (label == presubiculum     )
    return("presubiculum") ;
  if (label == subiculum      )
    return("subiculum") ;
  if (label == CA1        )
    return("CA1") ;
  if (label == CA2        )
    return("CA2") ;
  if (label == CA3        )
    return("CA3") ;
  if (label == CA4        )
    return("CA4") ;
  if (label == GC_DG      )
    return("GC_DG") ;
  if (label == HATA       )
    return("HATA") ;
  if (label == fimbria  )
    return("fimbria") ;
  if (label == lateral_ventricle    )
    return("lateral_ventricle") ;
  if (label == molecular_layer_HP  )
    return("molecular_layer_HP") ;
  if (label == hippocampal_fissure  )
    return("hippocampal_fissure") ;
  if (label == entorhinal_cortex  )
    return("entorhinal_cortex") ;
  if (label == molecular_layer_subiculum)
    return("molecular_layer_subiculum") ;
  if (label == Amygdala)
    return("Amygdala") ;
  if (label == Cerebral_White_Matter  )
    return("Cerebral_White_Matter") ;
  if (label == Cerebral_Cortex  )
    return("Cerebral_Cortex") ;
  if (label == Inf_Lat_Vent  )
    return("Inf_Lat_Vent") ;
#if 0
  if (Left_hippocampal_fissure == label)
    return("Left_hippocampal_fissure") ;
  if (Left_CADG_head == label)
    return("Left_CADG_head") ;
  if (Left_subiculum == label)
    return("Left_subiculum") ;
  if (Left_fimbria == label)
    return("Left_fimbria") ;
  if (Right_hippocampal_fissure == label)
    return("Right_hippocampal_fissure") ;
  if (Right_CADG_head == label)
    return("Right_CADG_head") ;
  if (Right_subiculum == label)
    return("Right_subiculum") ;
  if (Right_fimbria == label)
    return("Right_fimbria") ;
#endif
  if (Fornix == label)
    return("Fornix") ;

  if (label == BA17)
    return("BA17") ;
  if (label == BA18)
    return("BA18") ;
  if (label == BA44)
    return("BA44") ;
  if (label == BA45)
    return("BA45") ;
  if (label == BA4a)
    return("BA4a") ;
  if (label == BA4p)
    return("BA4p") ;
  if (label == BA6)
    return("BA6") ;
  if (label == BA2)
    return("BA2") ;
  if (label == BAun1)
    return("BAun1") ;
  if (label == BAun2)
    return("BAun2") ;
  if (label == right_CA2_3)
    return("right_CA2_3") ;
  if (label == right_alveus)
    return("right_alveus") ;
  if (label == right_CA1)
    return("right_CA1") ;
  if (label == right_fimbria)
    return("right_fimbria") ;
  if (label == right_presubiculum)
    return("right_presubiculum") ;
  if (label == right_hippocampal_fissure)
    return("right_hippocampal_fissure") ;
  if (label == right_CA4_DG)
    return("right_CA4_DG") ;
  if (label == right_subiculum)
    return("right_subiculum") ;
  if (label == left_CA2_3)
    return("left_CA2_3") ;
  if (label == left_alveus)
    return("left_alveus") ;
  if (label == left_CA1)
    return("left_CA1") ;
  if (label == left_fimbria)
    return("left_fimbria") ;
  if (label == left_presubiculum)
    return("left_presubiculum") ;
  if (label == left_hippocampal_fissure)
    return("left_hippocampal_fissure") ;
  if (label == left_CA4_DG)
    return("left_CA4_DG") ;
  if (label == left_subiculum)
    return("left_subiculum") ;


  if (label == left_fornix)
    return("left_fornix") ;
  if (label == right_fornix)
    return("right_fornix") ;

  return(name) ;
}
MRI *
GCArelabel_cortical_gray_and_white(GCA *gca,
                                   MRI *mri_inputs,
                                   MRI *mri_src,
                                   MRI *mri_dst,
                                   TRANSFORM *transform)
{
  int      nchanged, x, y, z, width, height, depth, total_changed,
  label, xn, yn, zn, left, new_wm, new_gray ;
  MRI      *mri_tmp ;
  GCA_NODE *gcan ;
  GC1D     *gc_gray, *gc_wm ;
  float    vals[MAX_GCA_INPUTS], gray_dist, wm_dist ;
  float    grayPrior;
  float    wmPrior;
  int      xp, yp, zp;

  GCA_PRIOR *gcap = 0;

  if (mri_src != mri_dst)
    mri_dst = MRIcopy(mri_src, mri_dst) ;

  mri_tmp = MRIcopy(mri_dst, NULL) ;

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

  total_changed = new_wm = new_gray = 0 ;
  do
  {
    nchanged = 0 ;
    for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (x = 0 ; x < width ; x++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;

          label = nint(MRIgetVoxVal(mri_src, x, y, z, 0)) ;
          if (label != Left_Cerebral_Cortex &&
              label != Left_Cerebral_White_Matter &&
              label != Right_Cerebral_Cortex &&
              label != Right_Cerebral_White_Matter)
            continue ;

          load_vals(mri_inputs, x, y, z, vals, gca->ninputs);
          if (!GCAsourceVoxelToNode(gca, mri_dst,
                                    transform,
                                    x, y, z, &xn, &yn, &zn))
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            // label is left cortex or wm
            if (label == Left_Cerebral_Cortex ||
                label == Left_Cerebral_White_Matter)
            {
              left = 1 ;
              gc_gray =
                GCAfindGC(gca, xn, yn, zn,
                          Left_Cerebral_Cortex) ;
              gc_wm =
                GCAfindGC(gca, xn, yn, zn,
                          Left_Cerebral_White_Matter) ;
            }
            // label is right cortex or wm
            else
            {
              gc_gray = GCAfindGC(gca, xn, yn, zn,
                                  Right_Cerebral_Cortex) ;
              gc_wm = GCAfindGC(gca, xn, yn, zn,
                                Right_Cerebral_White_Matter) ;
              left = 0 ;
            }
            // can't be found
            if (!gc_wm || !gc_gray)
              continue ;
            // calculate Mahalanobis distance
            gray_dist = sqrt(GCAmahDistIdentityCovariance
                             (gc_gray,
                              vals,
                              gca->ninputs)) ;
            wm_dist = sqrt(GCAmahDistIdentityCovariance
                           (gc_wm,
                            vals, gca->ninputs)) ;

            // get the prior coordinate
            if (!GCAsourceVoxelToPrior(gca, mri_dst,
                                       transform,
                                       x, y, z, &xp, &yp, &zp))
            {
              gcap = &gca->priors[xp][yp][zp];
              if (gcap==NULL)
                continue;
              // labeling gray but check Prior
              if (left)
              {
                grayPrior = getPrior(gcap, Left_Cerebral_Cortex);
                wmPrior = getPrior(gcap,
                                   Left_Cerebral_White_Matter);
              }
              else
              {
                grayPrior = getPrior(gcap,
                                     Right_Cerebral_Cortex);
                wmPrior = getPrior(gcap,
                                   Right_Cerebral_White_Matter);
              }
            }
            else
            {
              grayPrior = -1;
              wmPrior = -1;
            }
            // if grey < wm
            if (gray_dist < wm_dist && grayPrior > 0 && wmPrior > 0)
              // if ((5*gray_dist) < wm_dist &&
              // grayPrior > 0 && wmPrior > 0)
            {
              // if prior is not high, then you
              // can change white->gray
              if (wmPrior < 0.9)
                // label cortex
                label = left ? Left_Cerebral_Cortex :
                        Right_Cerebral_Cortex ;
              // else
              // printf("(%d,%d,%d) had gray_dist(%.2f) "
              //"< wm_dist (%.2f),
              // but grayPrior(%.2f) < wmPrior(%.2f)\n",
              // x, y, z, gray_dist, wm_dist, grayPrior, wmPrior);
            }
            else
            {
              // if prior is not high,
              // then you can change gray->white
              if (grayPrior < 0.9 && grayPrior > 0 && wmPrior > 0)
                // label wm
                //if ((5*wm_dist < gray_dist)
                // && grayPrior > 0 && wmPrior > 0)
                label =
                  left ? Left_Cerebral_White_Matter : \
                  Right_Cerebral_White_Matter ;
              // else
              // printf("(%d,%d,%d) had gray_dist(%.2f) > "
              //"wm_dist (%.2f), but grayPrior(%.2f)
              // > wmPrior(%.2f)\n",
              //    x, y, z, gray_dist, wm_dist,
              // grayPrior, wmPrior);
            }
            // if label changed from the current one
            // and it is possible
            if (label != nint(MRIgetVoxVal(mri_dst, x, y, z,0)) && \
                GCAisPossible(gca, mri_dst, label,
                              transform, x, y, z,0))
            {
              if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
                  (label == Ggca_label || Ggca_label < 0))
              {
                int in ;
                VECTOR *v_means = 0;

                printf("relabel_cortical_gray_and_white: "
                       "voxel(%d,%d,%d), inputs=", x, y, z) ;
                for (in = 0 ;in < gca->ninputs ; in++)
                  printf("%2.1f ", vals[in]) ;
                printf("label=%s (%d)\n",
                       cma_label_to_name(label),label);
                printf(" gray_dist = %.2f, wm_dist = %.2f\n",
                       gray_dist, wm_dist);
                v_means = load_mean_vector(gc_gray,
                                           v_means,
                                           gca->ninputs);
                printf("v_means for gray = ");
                MatrixPrint(stdout, v_means);
                v_means = load_mean_vector(gc_wm,
                                           v_means,
                                           gca->ninputs);
                printf("v_means for wm = ");
                MatrixPrint(stdout, v_means);
                VectorFree(&v_means);
              }
              // count changed label
              if (label == Left_Cerebral_Cortex ||label == \
                  Right_Cerebral_Cortex)
                new_gray++ ;
              else
                new_wm++ ;
              nchanged++ ;
              MRIsetVoxVal(mri_tmp, x, y, z, 0, label) ;
            }
          }
          //////////////////////////////////
        }
      }
    }
    MRIcopy(mri_tmp, mri_dst) ;
    total_changed += nchanged ;
  }
  while (nchanged > 0) ;

  MRIfree(&mri_tmp) ;
  printf("%d gm and wm labels changed (%%%2.0f to gray, %%%2.0f to "
         "white out of all changed labels)\n",
         total_changed, 100.0f*(float)new_gray/total_changed,
         100.0f*(float)new_wm/total_changed) ;
  return(mri_dst) ;
}
int
GCAdump(GCA *gca,MRI *mri,int x, int y, int z,
        TRANSFORM *transform, FILE *fp, int verbose)
{
  int       xn, yn, zn, xp, yp, zp ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;

  if (!GCAsourceVoxelToNode(gca, mri, transform, x, y, z, &xn, &yn, &zn))
  {
    if (!GCAsourceVoxelToPrior(gca, mri, transform, x, y, z, &xp, &yp, &zp))
    {
      printf("\nGCA node at voxel (%d, %d, %d) --> node "
             "(%d, %d, %d), prior (%d, %d, %d)\n",
             x, y, z, xn, yn, zn, xp, yp, zp) ;
      gcan = &gca->nodes[xn][yn][zn] ;
      gcap = getGCAP(gca, mri, transform, x, y, z) ;
      if (gcap==NULL)
        printf("\nGCAdump: prior point is outside.\n");
      dump_gcan(gca, gcan, fp, verbose, gcap) ;
    }
    else
      printf("\nGCAdump: prior point is outside.\n");
  }
  else
    printf("\nGCAdump: node point is outside.\n");
  return(NO_ERROR) ;
}

#if 0
static GCA_SAMPLE *
gcaExtractRegionLabelAsSamples(GCA *gca, MRI *mri_labeled,
                               TRANSFORM *transform,
                               int *pnsamples, int label, int xp, int yp,
                               int zp, int wsize)
{
  int         i, nsamples, width, height, depth, x, y, z,
  xi, yi, zi, xk, yk, zk, whalf ;
  GCA_SAMPLE  *gcas ;
  GCA_PRIOR   *gcap ;
  GC1D        *gc ;

  width = mri_labeled->width ;
  height = mri_labeled->height ;
  depth = mri_labeled->depth ;
  whalf = (wsize-1)/2 ;
  gcap = &gca->priors[xp][yp][zp] ;

  TransformInvert(transform, mri_labeled) ;
  if (!GCApriorToSourceVoxel(gca, mri_labeled, transform,
                             xp, yp, zp, &x, &y, &z))
  {
    for (nsamples = 0, zk = -whalf  ; zk <= whalf ; zk++)
    {
      zi = mri_labeled->zi[z + zk] ;
      for (yk = -whalf ; yk <= whalf ; yk++)
      {
        yi = mri_labeled->yi[y + yk] ;
        for (xk = -whalf ; xk <= whalf ; xk++)
        {
          xi = mri_labeled->xi[x + xk] ;
          if (nint(MRIgetVoxVal(mri_labeled, xi, yi, zi, 0)) != label)
            continue ;
          if (xi == Ggca_x && yi == Ggca_y && zi == Ggca_z)
            DiagBreak() ;
          nsamples++ ;
        }
      }
    }
  }
  gcas = (GCA_SAMPLE *)calloc(nsamples, sizeof(GCA_SAMPLE)) ;
  if (!gcas)
    ErrorExit(ERROR_NOMEMORY,
              "gcaExtractLabelAsSamples(%d): could not allocate %d samples\n",
              label, nsamples) ;


  /* go through region again and fill in samples */
  for (i = 0, zk = -whalf  ; zk <= whalf ; zk++)
  {
    zi = mri_labeled->zi[z + zk] ;
    for (yk = -whalf ; yk <= whalf ; yk++)
    {
      yi = mri_labeled->yi[y + yk] ;
      for (xk = -whalf ; xk <= whalf ; xk++)
      {
        xi = mri_labeled->xi[x + xk] ;
        if (nint(MRIgetVoxVal(mri_labeled, xi, yi, zi, 0)) != label)
          continue ;
        if (xi == Ggca_x && yi == Ggca_y && zi == Ggca_z)
          DiagBreak() ;

        gcap = getGCAP(gca, mri_labeled, transform, xi, yi, zi) ;
        if (gcap==NULL)
          continue;
        if (!GCAsourceVoxelToPrior(gca, mri_labeled,
                                   transform, xi, yi, zi, &xp, &yp, &zp))
        {
          gc = GCAfindPriorGC(gca, xp, yp, zp, label) ;
          if (!gc || !gcap)
          {
            nsamples-- ;   /* shouldn't happen */
            continue ;
          }

          gcas[i].label = label ;
          gcas[i].xp = xp ;
          gcas[i].yp = yp ;
          gcas[i].zp = zp ;
          gcas[i].x = xi ;
          gcas[i].y = yi ;
          gcas[i].z = zi ;
          gcas[i].var = gc->var ;
          gcas[i].mean = gc->mean ;
          gcas[i].prior = getPrior(gcap, label) ;
          if (FZERO(gcas[i].prior))
            DiagBreak() ;
          i++ ;
        }// !GCAsourceVoxel
      }
    }
  }

  *pnsamples = nsamples ;
  return(gcas) ;
}
#endif

static GCA_SAMPLE *
gcaExtractThresholdedRegionLabelAsSamples(GCA *gca, MRI *mri_labeled,
    TRANSFORM *transform,
    int *pnsamples,
    int label, int xp, int yp,
    int zp, int wsize, float pthresh)
{
  int         i, width, height, depth, x, y, z,
  xi, yi, zi, xk, yk, zk, whalf, r, c, v ;
  int         nsamples = 0;
  GCA_SAMPLE  *gcas ;
  GCA_PRIOR   *gcap ;
  GC1D        *gc ;
  float       prior ;

  width = mri_labeled->width ;
  height = mri_labeled->height ;
  depth = mri_labeled->depth ;
  whalf = (wsize-1)/2 ;
  gcap = &gca->priors[xp][yp][zp] ;
  if (gcap==NULL)
    return NULL;
  TransformInvert(transform, mri_labeled) ;
  if (!GCApriorToSourceVoxel(gca, mri_labeled, transform,
                             xp, yp, zp, &x, &y, &z))
  {
    for (nsamples = 0, zk = -whalf  ; zk <= whalf ; zk++)
    {
      zi = mri_labeled->zi[z + zk] ;
      for (yk = -whalf ; yk <= whalf ; yk++)
      {
        yi = mri_labeled->yi[y + yk] ;
        for (xk = -whalf ; xk <= whalf ; xk++)
        {
          xi = mri_labeled->xi[x + xk] ;
          if (nint(MRIgetVoxVal(mri_labeled, xi, yi, zi, 0)) != label)
            continue ;
          if (xi == Ggca_x && yi == Ggca_y && zi == Ggca_z)
            DiagBreak() ;
          nsamples++ ;
        }
      }
    }
  }

  gcas = (GCA_SAMPLE *)calloc(nsamples, sizeof(GCA_SAMPLE)) ;
  if (!gcas)
    ErrorExit(ERROR_NOMEMORY,
              "gcaExtractLabelAsSamples(%d): could not allocate %d samples\n",
              label, nsamples) ;


  /* go through region again and fill in samples */
  for (i = 0, zk = -whalf  ; zk <= whalf ; zk++)
  {
    zi = mri_labeled->zi[z + zk] ;
    for (yk = -whalf ; yk <= whalf ; yk++)
    {
      yi = mri_labeled->yi[y + yk] ;
      for (xk = -whalf ; xk <= whalf ; xk++)
      {
        xi = mri_labeled->xi[x + xk] ;
        if (nint(MRIgetVoxVal(mri_labeled, xi, yi, zi, 0)) != label)
          continue ;
        if (xi == Ggca_x && yi == Ggca_y && zi == Ggca_z)
          DiagBreak() ;

        gcap = getGCAP(gca, mri_labeled, transform, xi, yi, zi) ;
        if (gcap==NULL)
          continue;
        if (!GCAsourceVoxelToPrior(gca, mri_labeled,
                                   transform, xi, yi, zi, &xp, &yp, &zp))
        {
          gc = GCAfindPriorGC(gca, xp, yp, zp, label) ;
          if (gcap)
            prior = getPrior(gcap, label) ;
          else
            prior = 0 ;
          if (!gc || !gcap || prior < pthresh)
          {
            nsamples-- ;   /* shouldn't happen */
            continue ;
          }

          gcas[i].label = label ;
          gcas[i].xp = xp ;
          gcas[i].yp = yp ;
          gcas[i].zp = zp ;
          gcas[i].x = xi ;
          gcas[i].y = yi ;
          gcas[i].z = zi ;
          gcas[i].means =
            (float *)calloc(gca->ninputs, sizeof(float)) ;
          gcas[i].covars =
            (float *)calloc((gca->ninputs*(gca->ninputs+1))/2,
                            sizeof(float)) ;
          if (!gcas[i].means || !gcas[i].covars)
            ErrorExit(ERROR_NOMEMORY,
                      "GCArenormalizeAdapative: could not allocate "
                      "mean (%d) and covariance (%d) matrices",
                      gca->ninputs, gca->ninputs*(gca->ninputs+1)/2) ;
          for (r = v = 0 ; r < gca->ninputs ; r++)
          {
            gcas[i].means[r] = gc->means[r] ;
            for (c = r ; c < gca->ninputs ; c++)
              gcas[i].covars[v] = gc->covars[v] ;
          }
          gcas[i].prior = prior ;
          if (FZERO(gcas[i].prior))
            DiagBreak() ;
          i++ ;
        }
      }
    }
  }

  *pnsamples = nsamples ;
  return(gcas) ;
}

static GCA_SAMPLE *
gcaExtractLabelAsSamples(GCA *gca, MRI *mri_labeled, TRANSFORM *transform,
                         int *pnsamples, int label)
{
  int         i, nsamples, width, height, depth,
  x, y, z, xp, yp, zp, n, r, c, v ;
  GCA_SAMPLE  *gcas ;
  GCA_PRIOR    *gcap ;
  GC1D        *gc ;

  width = mri_labeled->width ;
  height = mri_labeled->height ;
  depth = mri_labeled->depth ;

  for (nsamples = z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (nint(MRIgetVoxVal(mri_labeled, x, y, z, 0)) != label)
          continue ;
        nsamples++ ;
      }
    }
  }

  gcas = (GCA_SAMPLE *)calloc(nsamples, sizeof(GCA_SAMPLE)) ;
  if (!gcas)
    ErrorExit(ERROR_NOMEMORY,
              "gcaExtractLabelAsSamples(%d): could not allocate %d samples\n",
              label, nsamples) ;


  for (i = z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (nint(MRIgetVoxVal(mri_labeled, x, y, z, 0)) != label)
          continue ;

        if (!GCAsourceVoxelToPrior(gca, mri_labeled,
                                   transform, x, y, z, &xp, &yp, &zp))
        {
          gcap = &gca->priors[xp][yp][zp] ;
          if (gcap==NULL)
            continue;
          for (n = 0 ; n < gcap->nlabels ; n++)
          {
            if (gcap->labels[n] == label)
              break ;
          }

          gc = GCAfindPriorGC(gca, xp, yp, zp, label) ;
          if (n >= gcap->nlabels || !gc)
          {
            nsamples-- ;   /* doesn't exist at this location */
            continue ;   /* ?? */
          }
          gcas[i].label = label ;
          gcas[i].xp = xp ;
          gcas[i].yp = yp ;
          gcas[i].zp = zp ;
          gcas[i].x = x ;
          gcas[i].y = y ;
          gcas[i].z = z ;
          for (r = v = 0 ; r < gca->ninputs ; r++)
          {
            gcas[i].means[r] = gc->means[r] ;
            for (c = r ; c < gca->ninputs ; c++)
              gcas[i].covars[v] = gc->covars[v] ;
          }
          gcas[i].prior = getPrior(gcap, label) ;
          if (FZERO(gcas[i].prior))
            DiagBreak() ;
          i++ ;               // this i is never used??????
        } // !GCAsourceVoxelToPrior
      }
    }
  }

  *pnsamples = nsamples ;
  return(gcas) ;
}

#define MIN_MEAN_SAMPLES      10
#define SAMPLE_PCT       0.20

int
GCArenormalize(MRI *mri_in, MRI *mri_labeled, GCA *gca, TRANSFORM *transform)
{
  int              x, y, z, n, label, biggest_label, nsamples, val,
  *ordered_indices, i, index, width, height, depth ;
  float            mean, var, *means, *stds, *gca_means ;
  GCA_NODE         *gcan ;
  GCA_SAMPLE       *gcas ;
  GC1D             *gc ;

  if (gca->ninputs > 1)
    ErrorExit(ERROR_UNSUPPORTED,
              "GCArenormalize: can only renormalize scalars") ;

  /* first build a list of all labels that exist */
  for (nsamples = biggest_label = x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_height ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (gcan->labels[n] > biggest_label)
            biggest_label = gcan->labels[n] ;
        }
      }
    }
  }

  gca_means = (float *)calloc(biggest_label+1, sizeof(float)) ;
  means = (float *)calloc(biggest_label+1, sizeof(float)) ;
  stds = (float *)calloc(biggest_label+1, sizeof(float)) ;
  if (!gca_means || !means || !stds)
    ErrorExit(ERROR_NOMEMORY, "%s: could not allocated %d vector",
              Progname, biggest_label+1) ;

  /* do unknown labels separately */
  for (mean = var = 0.0, nsamples = x = 0 ; x < mri_in->width ; x++)
  {
    for (y = 0 ; y < mri_in->height ; y++)
    {
      for (z = 0 ; z < mri_in->height ; z++)
      {
        if (nint(MRIgetVoxVal(mri_labeled, x, y, z, 0)) == Unknown)
        {
          nsamples++ ;
          val = MRIgetVoxVal(mri_in, x, y, z, 0) ;
          mean += val ;
          var += (val*val) ;
        }
      }
    }
  }

  if (DIAG_VERBOSE_ON && 0)
  {
    HISTOGRAM *histo ;
    char  fname[STRLEN] ;

    label = Right_Hippocampus ;
    histo = MRIhistogramLabel(mri_in, mri_labeled, label, 256) ;
    sprintf(fname, "%s.plt", cma_label_to_name(label)) ;
    HISTOplot(histo, fname) ;
    HISTOfree(&histo) ;
  }

#if 1
  label = Unknown ;
  if (!FZERO(nsamples))
  {
    mean /= nsamples ;
    means[label] = mean ;
    stds[label] = sqrt(var/nsamples - mean*mean) ;
    GCAlabelMean(gca, label, &gca_means[label]) ;
    printf("scaling label %s by %2.2f (%2.2f / %2.2f) "
           "(%d samples, std=%2.1f)\n",
           cma_label_to_name(label),
           means[label] / gca_means[label],
           means[label], gca_means[label], nsamples, stds[label]) ;
  }
  else
  {
    gca_means[label] = stds[label] = means[label] = 1.0 ;
  }

  gca_means[label] = stds[label] = means[label] = 1.0 ;
#endif

  for (label = 1 ; label <= biggest_label ; label++)
  {
    gcas = gcaExtractLabelAsSamples(gca,
                                    mri_labeled,transform,&nsamples,label);
    if (!nsamples)
      continue ;
    if (nsamples < MIN_MEAN_SAMPLES)
      /* not enough sample to estimate mean */
    {
      free(gcas) ;
      continue ;
    }
    ordered_indices = (int *)calloc(nsamples, sizeof(int)) ;
    GCAcomputeLogSampleProbability(gca, gcas, mri_in, transform, nsamples) ;
    GCArankSamples(gca, gcas, nsamples, ordered_indices) ;

    if (nint(nsamples*SAMPLE_PCT) < MIN_MEAN_SAMPLES)
      nsamples = MIN_MEAN_SAMPLES ;
    else
      nsamples = nint(SAMPLE_PCT * (float)nsamples) ;


    /* compute mean and variance of image intensities in this label */
    for (var = mean = 0.0f, i = 0 ; i < nsamples ; i++)
    {
      index = ordered_indices[i] ;
      val = MRIgetVoxVal(mri_in,
												 gcas[index].x,
												 gcas[index].y,
												 gcas[index].z, 0);
      mean += val ;
      var += val*val ;
    }
    mean /= (float)nsamples ;
    var = var / nsamples - mean*mean ;
    var = sqrt(var);
#if 0
    printf("label %s: using %d samples to estimate mean = %2.1f +- %2.1f\n",
           cma_label_to_name(label), nsamples, mean, var) ;
#endif
    means[label] = mean ;
    stds[label] = var ;
    GCAlabelMean(gca, label, &gca_means[label]) ;
    free(gcas) ;
    free(ordered_indices) ;
    printf("scaling label %s by %2.2f (%2.2f / %2.2f) "
           "(%d samples, std=%2.1f)\n",
           cma_label_to_name(label),
           means[label] / gca_means[label],
           means[label], gca_means[label], nsamples, stds[label]) ;
    if (FZERO(gca_means[label]))
      DiagBreak() ;
  }

  width = gca->node_width ;
  height = gca->node_height ;
  depth = gca->node_depth ;
  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          label = gcan->labels[n] ;
          gc = &gcan->gcs[n] ;
          mean = gc->means[0] * means[label] / gca_means[label] ;
          gc->means[0] = mean ;
        }
      }
    }
  }

  free(means) ;
  free(stds) ;
  free(gca_means) ;
  return(NO_ERROR) ;
}
int
GCArenormalizeAdaptive(MRI *mri_in, MRI *mri_labeled,
                       GCA *gca, TRANSFORM *transform,
                       int wsize, float pthresh)
{
  int              x, y, z, n, label, xp,yp, zp, peak, orig_wsize, frame ;
#if 0
  int              i, index, *ordered_indices ;
  float            mean, var ;
  Real             val ;
#endif
  GCA_NODE         *gcan ;
  GCA_SAMPLE       *gcas ;
  GC1D             *gc ;
  HISTOGRAM        *histo, *hsmooth ;
  float            fmin, fmax ;
  int              nsamples=0;

  orig_wsize = wsize ;
  MRIvalRange(mri_in, &fmin, &fmax) ;
  histo = HISTOalloc((int)(fmax-fmin+1)) ;
  hsmooth = HISTOalloc((int)(fmax-fmin+1)) ;

  /* go through GCA renormalizing each entry */
  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          label = gcan->labels[n] ;
          if (label == Unknown)
            continue ;
          if ( gcaNodeToPrior(gca, x, y, z, &xp, &yp, &zp)==NO_ERROR)
          {
            gc = &gcan->gcs[n] ;

#define MIN_SAMPLES 20
            wsize = orig_wsize ;
            if (label == Ggca_label)
              DiagBreak() ;
            do
            {
              gcas =
                gcaExtractThresholdedRegionLabelAsSamples
                (gca, \
                 mri_labeled,
                 transform,
                 &nsamples,
                 label,
                 xp, yp, zp,
                 wsize,pthresh);

              if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
                DiagBreak() ;
              wsize += 2;
              if (gcas && nsamples < MIN_SAMPLES)
                GCAfreeSamples(&gcas, nsamples) ;
            }
            while ((nsamples < MIN_SAMPLES) \
                   && (wsize < 2*orig_wsize));

            if (nsamples < MIN_SAMPLES)
              /* couldn't find any in this nbhd */
              continue ;

            for (frame = 0 ; frame < gca->ninputs ; frame++)
            {
              gcaHistogramSamples(gca, gcas, mri_in,
                                  transform, nsamples,
                                  histo, frame);
              HISTOsmooth(histo, hsmooth, 2) ;
              if (IS_WM(label) && gca->ninputs == 1)
                peak = HISTOfindLastPeakRelative
                       (hsmooth,
                        HISTO_WINDOW_SIZE,.3);
              else if (IS_LAT_VENT(label) && gca->ninputs == 1)
                peak = HISTOfindFirstPeakRelative
                       (hsmooth,
                        HISTO_WINDOW_SIZE,.3);
              else
                peak =
                  HISTOfindHighestPeakInRegion(hsmooth,
                                               0,
                                               (fmax-fmin)*\
                                               hsmooth->bin_size) ;
              if (peak < 0)
                continue ;
#if 0
              ordered_indices =
                (int *)calloc(nsamples, sizeof(int)) ;
              GCAcomputeLogSampleProbability(gca,
                                             gcas,
                                             mri_in,
                                             transform,
                                             nsamples) ;
              GCArankSamples(gca, gcas, nsamples,
                             ordered_indices) ;

              if (nsamples > MIN_SAMPLES)
              {
                if (nint(nsamples*SAMPLE_PCT) < MIN_SAMPLES)
                  nsamples = MIN_SAMPLES ;
                else
                  nsamples = nint(SAMPLE_PCT * (float)nsamples) ;
              }


              /* compute mean and variance of image */
              /*intensities in this label */
              for (var = mean = 0.0f, i = 0 ; i < nsamples ; i++)
              {
                index = ordered_indices[i] ;
                MRIsampleVolumeFrame(mri_in,
                                     gcas[index].x,
                                     gcas[index].y,
                                     gcas[index].z,
                                     frame, &val);
                mean += val ;
                var += val*val ;
                if (gcas[index].x == Ggca_x &&
                    gcas[index].y == Ggca_y &&
                    gcas[index].z == Ggca_z)
                  DiagBreak() ;
              }
              mean /= (float)nsamples ;
              var = var / nsamples - mean*mean ;
              var = sqrt(var);
              free(ordered_indices) ;
              gc->means[0] = mean ;
#endif
              gc->means[frame] = (float)peak ;
            }
            GCAfreeSamples(&gcas, nsamples) ;
          }
        }
      }
    }
  }

  HISTOfree(&histo) ;
  HISTOfree(&hsmooth) ;
  return(NO_ERROR) ;
}


#define MEAN_RESOLUTION  8

int
GCArenormalizeLabels(MRI *mri_in,
                     MRI *mri_labeled,
                     GCA *gca,
                     TRANSFORM *transform)
{
  int              x, y, z, n, label, biggest_label, nsamples, xv, yv, zv,
  *ordered_indices, i, index, width, height, depth ;
  float            mean, var, val ;
  GCA_NODE         *gcan ;
  GCA_SAMPLE       *gcas ;
  GC1D             *gc ;
  MRI              *mri_means, *mri_control, *mri_tmp ;
  char             fname[STRLEN] ;

  if (gca->ninputs > 1)
    ErrorExit(ERROR_UNSUPPORTED,
              "GCArenormalizeLabels: can only renormalize scalars") ;

  /* first build a list of all labels that exist */
  for (biggest_label = x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_height ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (gcan->labels[n] > biggest_label)
            biggest_label = gcan->labels[n] ;
        }
      }
    }
  }

  for (label = 1 ; label <= biggest_label ; label++)
  {
    gcas = gcaExtractLabelAsSamples(gca, mri_labeled,
                                    transform,&nsamples,label);
    if (!nsamples)
      continue ;
    if (nsamples < MIN_SAMPLES/SAMPLE_PCT)
    {
      free(gcas) ;
      continue ;
    }
    ordered_indices = (int *)calloc(nsamples, sizeof(int)) ;
    GCAcomputeLogSampleProbability(gca, gcas, mri_in, transform, nsamples) ;
    GCArankSamples(gca, gcas, nsamples, ordered_indices) ;

    if (nint(nsamples*SAMPLE_PCT) < MIN_SAMPLES)
      nsamples = MIN_SAMPLES ;
    else
      nsamples = nint(SAMPLE_PCT * (float)nsamples) ;


    for (var = mean = 0.0f, i = 0 ; i < nsamples ; i++)
    {
      index = ordered_indices[i] ;
      val = MRIgetVoxVal(mri_in,
                          gcas[index].x,
                          gcas[index].y,
                          gcas[index].z, 0);
      mean += val ;
      var += val*val ;
    }
    mean /= (float)nsamples ;
    var = var / nsamples - mean*mean ;
    var = sqrt(var);
    printf("label %s: using %d samples to estimate mean = %2.1f +- %2.1f\n",
           cma_label_to_name(label), nsamples, mean, var) ;

    width = nint(mri_in->width / MEAN_RESOLUTION) ;
    height = nint(mri_in->height / MEAN_RESOLUTION) ;
    depth = nint(mri_in->depth / MEAN_RESOLUTION) ;
    mri_means = MRIalloc(width, height, depth, MRI_FLOAT) ;
    MRIcopyHeader(mri_in, mri_means);
    mri_control = MRIalloc(width, height, depth, MRI_SHORT) ;
    MRIcopyHeader(mri_in, mri_control);
    MRIsetResolution(mri_means,
                     MEAN_RESOLUTION,MEAN_RESOLUTION,MEAN_RESOLUTION) ;
    MRIsetResolution(mri_control,
                     MEAN_RESOLUTION,MEAN_RESOLUTION,MEAN_RESOLUTION) ;

    GCAlabelMri(gca, mri_means, label, transform) ;
    if (DIAG_VERBOSE_ON && Gdiag & DIAG_WRITE)
    {
      sprintf(fname, "%s_label.mgz", cma_label_to_name(label)) ;
      MRIwrite(mri_means, fname) ;
    }

    for (mean = 0.0f, n = z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (x = 0 ; x < width ; x++)
        {
          if (MRIFvox(mri_means, x, y, z) > 1)
          {
            n++ ;
            mean += MRIFvox(mri_means, x, y, z) ;
          }
        }
      }
    }
    mean /= (float)n ;
    printf("mean GCA value %2.1f\n", mean) ;
    for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (x = 0 ; x < width ; x++)
        {
          if (MRIFvox(mri_means, x, y, z) < 1)
          {
            MRIFvox(mri_means, x, y, z) = mean ;
          }
        }
      }
    }

    TransformInvert(transform, mri_in) ;
    for (i = 0 ; i < nsamples ; i++)
    {
      index = ordered_indices[i] ;
      if (!GCApriorToSourceVoxel(gca, mri_means, transform,
                                 gcas[index].xp,
                                 gcas[index].yp,
                                 gcas[index].zp,
                                 &x, &y, &z))
      {
        val = MRIgetVoxVal(mri_in,
													 gcas[index].x,
													 gcas[index].y,
													 gcas[index].z, 0);
        if (x == 19 && y == 14 && z == 15)
          DiagBreak() ;
        if (MRISvox(mri_control, x, y, z) == 0)
          MRIFvox(mri_means, x, y, z) = val ;
        else
          MRIFvox(mri_means, x, y, z) += val ;
        MRISvox(mri_control, x, y, z)++ ;
      }
    }

    for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (x = 0 ; x < width ; x++)
        {
          if (x == 19 && y == 14 && z == 15)
            DiagBreak() ;
          if (MRISvox(mri_control, x, y, z) > 0)
          {
            MRIFvox(mri_means,x,y,z) =
              nint((float)MRIFvox(mri_means,x,y,z)/
                   (float)MRISvox(mri_control,x,y,z)) ;
            MRISvox(mri_control, x, y, z) = 1 ;
          }
        }
      }
    }

    if (DIAG_VERBOSE_ON && Gdiag & DIAG_WRITE)
    {
      sprintf(fname, "%s_means.mgz", cma_label_to_name(label)) ;
      MRIwrite(mri_means, fname) ;
    }

    mri_tmp = MRIalloc(width, height, depth, MRI_SHORT) ;
    MRIcopy(mri_means, mri_tmp) ;
    MRIfree(&mri_means) ;
    mri_means = mri_tmp ;

    mri_tmp = MRIalloc(width, height, depth, MRI_UCHAR) ;
    MRIcopy(mri_control, mri_tmp) ;
    MRIfree(&mri_control) ;
    mri_control = mri_tmp ;

    MRIsoapBubble(mri_means, mri_control, mri_means, 10) ;
    MRIclear(mri_control) ;
    MRIsoapBubble(mri_means, mri_control, mri_means, 1) ;

    if (DIAG_VERBOSE_ON && Gdiag & DIAG_WRITE)
    {
      sprintf(fname, "%s_soap.mgz", cma_label_to_name(label)) ;
      MRIwrite(mri_means, fname) ;

      sprintf(fname, "%s_control.mgz", cma_label_to_name(label)) ;
      MRIwrite(mri_control, fname) ;
    }


    width = gca->node_width ;
    height = gca->node_height ;
    depth = gca->node_depth ;
    for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (x = 0 ; x < width ; x++)
        {
          if (x == Gx && y == Gy && z == Gz)
            DiagBreak() ;
          gcan = &gca->nodes[x][y][z] ;
          for (n = 0 ; n < gcan->nlabels ; n++)
          {
            if (gcan->labels[n] == label)
            {
              if (x == Gx && y == Gy && z == Gz)
                DiagBreak() ;
              gc = &gcan->gcs[n] ;
              if (GCAnodeToVoxel(gca, mri_means,
                                 x, y, z,
                                 &xv, &yv, &zv)==NO_ERROR)
                gc->means[0] =
                  (float)MRISvox(mri_means, xv, yv, zv);
              break ;
            }
          }
        }
      }
    }

    MRIfree(&mri_means) ;
    MRIfree(&mri_control) ;
    free(gcas) ;
    free(ordered_indices) ;
  }

  return(NO_ERROR) ;
}

int
GCArenormalizeIntensities(GCA *gca, int *labels, float *intensities, int num)
{
  float     wm_mean, scale_factor, gray_mean, prior ;
  int       xn, yn, zn, n, i, label ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  double    *means, *wts ;

  if (gca->ninputs > 1)
    ErrorExit(ERROR_UNSUPPORTED,
              "GCArenormalizeIntensities: can only renormalize scalars") ;

  means = (double *)calloc(num, sizeof(double)) ;
  wts = (double *)calloc(num, sizeof(double)) ;
  if (!means || !wts)
    ErrorExit(ERROR_NOMEMORY, "%s: could not allocate %d wt and mean vectors",
              Progname, num) ;

  /* compute overall white matter mean to use as anchor for rescaling */
  for (zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          /* find index in lookup table for this label */
          label = gcan->labels[n] ;
          for (i = 0 ; i < num ; i++)
            if (label == labels[i])
              break ;

          if (i >= num)
            continue ;  /* shouldn't happen */

          gc = &gcan->gcs[n] ;
          prior = get_node_prior(gca, label, xn, yn, zn);
          if (prior != 0)
          {
            wts[i] += prior ;
            means[i] += gc->means[0]*prior ;
          }
        }
      }
    }
  }
  gray_mean = scale_factor = wm_mean = -1 ;
  for (i = 0 ; i < num ; i++)
  {
    if (!FZERO(wts[i]))
      means[i] /= wts[i] ;
    if (labels[i] == Left_Cerebral_White_Matter)
    {
      wm_mean = means[i] ;
      scale_factor = wm_mean / intensities[i] ;
    }
    if (labels[i] == Left_Cerebral_Cortex)
      gray_mean = means[i] ;
  }
  if (wm_mean < 0)
    ErrorReturn(ERROR_BADPARM,
                (ERROR_BADPARM, "GCArenormalizeIntensities: "
                 "could not find white matter in intensity table")) ;
  scale_factor = 1 ;
  printf("mean wm intensity = %2.1f, scaling gray to %2.2f (from %2.2f)\n",
         wm_mean, scale_factor*gray_mean, gray_mean) ;

  /* now go through each label and scale it by gca wm to norm wm ratio */
  /* compute overall white matter mean to use as anchor for rescaling */
  for (zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z)
          DiagBreak() ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          label = gcan->labels[n] ;
          if (label == Gdiag_no)
            DiagBreak() ;

          /* find index in lookup table for this label */
          for (i = 0 ; i < num ; i++)
            if (label == labels[i])
              break ;
          if (i >= num)
            continue ;

          gc = &gcan->gcs[n] ;
          gc->means[0] =
            scale_factor*intensities[i]*gc->means[0]/means[i] ;
        }
      }
    }
  }

  free(wts) ;
  free(means) ;
  return(NO_ERROR) ;
}

int
GCAunifyVariance(GCA *gca)
{
  int        xn, yn, zn, n, r, c, v ;
  GCA_NODE   *gcan ;
  GC1D       *gc ;

  for (zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          gc = &gcan->gcs[n] ;
          for (r = v = 0 ; r < gca->ninputs ; r++)
          {
            for (c = r ; c < gca->ninputs ; c++, v++)
            {
              if (r == c)
                gc->covars[v] = MIN_VAR ;
              else
                gc->covars[v] = 0.0 ;
            }
          }
        }
      }
    }
  }

  return(NO_ERROR) ;
}


int
GCAlabelMode(GCA *gca, int label, float *modes)
{
  int       xn, yn, zn, n, r ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  float     prior ;
  HISTOGRAM *h ;
  int       b ;

  h = HISTOalloc(256) ;
  for (b = 0 ; b < h->nbins ; b++)
    h->bins[b] = b ;

  memset(modes, 0, gca->ninputs*sizeof(float)) ;
  for (zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          /* find index in lookup table for this label */
          if (gcan->labels[n] != label)
            continue ;
          gc = &gcan->gcs[n] ;
          prior = get_node_prior(gca, label, xn, yn, zn) ;
          if (prior != 0)
          {
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              b = nint(gc->means[r]) ;
              h->counts[b] += prior ;
              if (!finite(gc->means[r]))
                DiagBreak() ;
            }
          }

        }
      }
    }
  }
  if (Gdiag & DIAG_WRITE)
  {
    char  fname[STRLEN] ;
    sprintf(fname, "gca_label%d.plt", label) ;
    HISTOplot(h, fname) ;
  }

  for (r = 0 ; r < gca->ninputs ; r++)
  {
    b = HISTOfindHighestPeakInRegion(h, 0, h->nbins) ;
    modes[r] = h->bins[b] ;
  }
  return(NO_ERROR) ;
}
int
GCAclassMode(GCA *gca, int class, float *modes)
{
  int       xn, yn, zn, n, r ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  float     prior ;
  HISTOGRAM *h ;
  int       b, label ;

  h = HISTOalloc(256) ;
  for (b = 0 ; b < h->nbins ; b++)
    h->bins[b] = b ;

  memset(modes, 0, gca->ninputs*sizeof(float)) ;
  for (zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          label = gcan->labels[n] ;
          switch (class)  // check to make sure it is
            // the specified class
          {
          case WM_CLASS:
            if (IS_WHITE_CLASS(gcan->labels[n]) == 0)
              continue ;
            break ;
          case GM_CLASS:
            if (IS_GRAY_CLASS(gcan->labels[n]) == 0)
              continue ;
            break ;
          case CSF_CLASS:
            if (IS_CSF_CLASS(gcan->labels[n]) == 0)
              continue ;
            break ;
          default:
            break ;
          }
          prior = get_node_prior(gca, label, xn, yn, zn) ;
          gc = GCAfindGC(gca, xn, yn, zn, label) ;
          if (gc == NULL)
            continue ;
          if (prior != 0)
          {
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              b = nint(gc->means[r]) ;
              h->counts[b] += prior ;
              if (!finite(gc->means[r]))
                DiagBreak() ;
            }
          }

        }
      }
    }
  }
  if (Gdiag & DIAG_WRITE)
  {
    char  fname[STRLEN] ;
    sprintf(fname, "gca_label%d.plt", class) ;
    HISTOplot(h, fname) ;
  }

  for (r = 0 ; r < gca->ninputs ; r++)
  {
    b = HISTOfindHighestPeakInRegion(h, 0, h->nbins) ;
    modes[r] = h->bins[b] ;
  }
  return(NO_ERROR) ;
}

int
GCAlabelMean(GCA *gca, int label, float *means)
{
  int       xn, yn, zn, n, r ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  double    wt ;
  float     prior ;

  /* compute overall white matter mean to use as anchor for rescaling */
  memset(means, 0, gca->ninputs*sizeof(float)) ;
  for (wt = 0.0, zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          /* find index in lookup table for this label */
          if (gcan->labels[n] != label)
            continue ;
          gc = &gcan->gcs[n] ;
          prior = get_node_prior(gca, label, xn, yn, zn) ;
          if (prior != 0)
          {
            wt += prior ;
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              means[r] += gc->means[r]*prior ;
              if (!finite(gc->means[r]))
                DiagBreak() ;
            }
          }

        }
      }
    }
  }
  if (FZERO(wt))
    return(NO_ERROR) ;
  for (r = 0 ; r < gca->ninputs ; r++)
    means[r] /= wt ;
  return(NO_ERROR) ;
}
int
GCAclassMean(GCA *gca, int class, float *means)
{
  int       xn, yn, zn, n, r, label ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  double    wt ;
  float     prior ;

  /* compute overall white matter mean to use as anchor for rescaling */
  memset(means, 0, gca->ninputs*sizeof(float)) ;
  for (wt = 0.0, zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          label = gcan->labels[n] ;
          switch (class)  // check to make sure it is
            // the specified class
          {
          case WM_CLASS:
            if (IS_WHITE_CLASS(gcan->labels[n]) == 0)
              continue ;
            break ;
          case GM_CLASS:
            if (IS_GRAY_CLASS(gcan->labels[n]) == 0)
              continue ;
            break ;
          case CSF_CLASS:
            if (IS_CSF_CLASS(gcan->labels[n]) == 0)
              continue ;
            break ;
          default:
            break ;
          }
          /* find index in lookup table for this label */
          gc = &gcan->gcs[n] ;
          prior = get_node_prior(gca, label, xn, yn, zn) ;
          if (prior != 0)
          {
            wt += prior ;
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              means[r] += gc->means[r]*prior ;
              if (!finite(gc->means[r]))
                DiagBreak() ;
            }
          }

        }
      }
    }
  }
  if (FZERO(wt))
    return(NO_ERROR) ;
  for (r = 0 ; r < gca->ninputs ; r++)
    means[r] /= wt ;
  return(NO_ERROR) ;
}

int
GCAregularizeConditionalDensities(GCA *gca, float smooth)
{
  int       xn, yn, zn, n, i, label, max_label, r ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  double    **means, *wts ;
  float     prior ;

  means = (double **)calloc(gca->ninputs, sizeof(double *)) ;
  wts = (double *)calloc(MAX_GCA_LABELS, sizeof(double)) ;
  if (!means || !wts)
    ErrorExit(ERROR_NOMEMORY, "%s: could not allocate %d wt and mean vectors",
              Progname, MAX_GCA_LABELS) ;
  for (r = 0 ; r < gca->ninputs ; r++)
  {
    means[r] = (double *)calloc(MAX_GCA_LABELS, sizeof(double)) ;
    if (!means[r])
      ErrorExit(ERROR_NOMEMORY,
                "%s: could not allocate %d wt and mean vectors",
                Progname, MAX_GCA_LABELS) ;
  }

  /* compute overall mean for each class */
  for (max_label = 1, zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          /* find index in lookup table for this label */
          label = gcan->labels[n] ;

          gc = &gcan->gcs[n] ;
          prior = get_node_prior(gca, label, xn, yn, zn) ;
          if (prior != 0)
          {
            wts[label] += prior ;
            for (r = 0 ; r < gca->ninputs ; r++)
              means[r][label] += gc->means[r]*prior ;
            if (label > max_label)
              max_label = label ;
          }
        }
      }
    }
  }

  for (i = 0 ; i <= max_label ; i++)
  {
    if (!FZERO(wts[i]))
    {
      for (r = 0 ; r < gca->ninputs ; r++)
        means[r][i] /= wts[i] ;
    }
  }

  /* now impose regularization */
  for (zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          /* find index in lookup table for this label */
          label = gcan->labels[n] ;
          if (label <= 0)
            continue ;

          gc = &gcan->gcs[n] ;
          for (r = 0 ; r < gca->ninputs ; r++)
            gc->means[r] =
              means[r][label]*smooth + gc->means[r]*(1.0f-smooth) ;
        }
      }
    }
  }

  for (r = 0 ; r < gca->ninputs ; r++)
    free(means[r]) ;
  free(wts) ;
  free(means) ;
  return(NO_ERROR) ;
}
int
GCArenormalizeToFlash(GCA *gca, char *tissue_parms_fname, MRI *mri)
{
  FILE     *fp ;
  char     *cp, line[STRLEN] ;
  int      labels[MAX_GCA_LABELS], nlabels ;
  float   intensities[MAX_GCA_LABELS], TR, alpha, T1, PD ;

  TR = mri->tr ;
  alpha = mri->flip_angle ;

  fp = fopen(tissue_parms_fname, "r") ;
  if (!fp)
    ErrorReturn(ERROR_NOFILE,
                (ERROR_NOFILE,
                 "GCArenormalizeToFlash: could not open tissue parms file %s",
                 tissue_parms_fname)) ;

  cp = fgetl(line, STRLEN-1, fp) ;
  nlabels = 0 ;
  while (cp)
  {
    if (sscanf(cp, "%d %f %f", &labels[nlabels], &T1, &PD)
        != 3)
      ErrorReturn(ERROR_BADFILE,
                  (ERROR_BADFILE,
                   "GCArenormalizeToFlash: "
                   "could not parse %dth line %s in %s",
                   nlabels+1, cp, tissue_parms_fname)) ;

    intensities[nlabels] = FLASHforwardModel(T1, PD, TR, alpha, 3) ;

    nlabels++ ;
    cp = fgetl(line, STRLEN-1, fp) ;
  }
  fclose(fp) ;
  GCArenormalizeIntensities(gca, labels, intensities, nlabels) ;
  return(NO_ERROR) ;
}

int
GCAmeanFilterConditionalDensities(GCA *gca, float navgs)
{
  /* won't work for covariances */
  int       xn, yn, zn, xn1, yn1, zn1, n, label, max_label, niter, f ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  double    means[MAX_GCA_INPUTS], wt ;
  MRI       *mri_means ;
  float     prior ;

  mri_means = MRIallocSequence(gca->node_width,
                               gca->node_height,
                               gca->node_depth,
                               MRI_FLOAT, gca->ninputs) ;

  mri_means->xsize = gca->node_spacing;
  mri_means->ysize = gca->node_spacing;
  mri_means->zsize = gca->node_spacing;

  GCAcopyDCToMRI(gca, mri_means);

  /* compute overall mean for each class */
  for (max_label = 1, zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        if (xn == Gx && yn == Gy && zn == Gz)
          DiagBreak() ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          /* find index in lookup table for this label */
          label = gcan->labels[n] ;

          gc = &gcan->gcs[n] ;
          if (label > max_label)
            max_label = label ;
        }
      }
    }
  }

  /* now impose regularization */
  for (niter = 0 ; niter < navgs ; niter++)
  {
    for (label = 0 ; label <= max_label ; label++)
    {
      if (label == Gdiag_no)
        DiagBreak() ;
      if (IS_UNKNOWN(label))
        continue ;
      for (zn = 0 ; zn < gca->node_depth ; zn++)
      {
        for (yn = 0 ; yn < gca->node_height ; yn++)
        {
          for (xn = 0 ; xn < gca->node_width ; xn++)
          {
            if (xn == Gx && yn == Gy && zn == Gz)
              DiagBreak() ;
            wt = 0.0 ;
            for (f = 0 ; f < gca->ninputs ; f++)
              means[f] = 0 ;
            for (xn1 = xn-1 ; xn1 <= xn+1 ; xn1++)
            {
              if (xn1 < 0 || xn1 >= gca->node_width)
                continue ;
              for (yn1 = yn-1 ; yn1 <= yn+1 ; yn1++)
              {
                if (yn1 < 0 || yn1 >= gca->node_height)
                  continue ;
                for (zn1 = zn-1 ; zn1 <= zn+1 ; zn1++)
                {
                  if (zn1 < 0 || zn1 >= gca->node_depth)
                    continue ;
                  if (xn1 == xn && yn1 == yn && zn1 == zn)
                    DiagBreak() ;
                  gcan = &gca->nodes[xn1][yn1][zn1] ;
                  for (n = 0 ; n < gcan->nlabels ; n++)
                  {
                    /* find index in lookup table
                       for this label */
                    if (gcan->labels[n] != label)
                      continue ;

                    gc = &gcan->gcs[n] ;
                    prior = get_node_prior(gca, label,
                                           xn1, yn1, zn1) ;
                    if (prior != 0)
                    {
                      for (f = 0 ; f < gca->ninputs ; f++)
                      {
                        means[f] += prior*gc->means[f] ;
                        if (!finite(means[f] / wt))
                          DiagBreak() ;
                      }
                      wt += prior ;
                      break ;
                    }
                  }
                }
              }
            }
            if (FZERO(wt))
              continue ;   /* label didn't occur here */
            for (f = 0 ; f < gca->ninputs ; f++)
            {
              if (!finite(means[f] / wt))
                DiagBreak() ;
              MRIFseq_vox(mri_means, xn, yn, zn, f)
              = means[f] / wt ;
              if (!finite(MRIFseq_vox(mri_means,xn,yn,zn,f)))
                DiagBreak() ;
            }
          }
        }
      }

      for (zn = 0 ; zn < gca->node_depth ; zn++)
      {
        for (yn = 0 ; yn < gca->node_height ; yn++)
        {
          for (xn = 0 ; xn < gca->node_width ; xn++)
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            for (n = 0 ; n < gcan->nlabels ; n++)
            {
              if (gcan->labels[n] != label)
                continue ;
              gc = &gcan->gcs[n] ;
              if (xn == Gx && yn == Gy && zn == Gz)
                DiagBreak() ;
              for (f = 0 ; f < gca->ninputs ; f++)
              {
                gc->means[f] =
                  MRIFseq_vox(mri_means, xn, yn, zn, f) ;
                if (!finite(gc->means[f]))
                  DiagBreak() ;
              }
              break ;
            }
          }
        }
      }
    }
  }

  MRIfree(&mri_means) ;
  return(NO_ERROR) ;
}

#define OFFSET_SIZE  25
double BOX_SIZE =   60;   /* mm */
double HALF_BOX=   (60/2);
int
GCAhistoScaleImageIntensities(GCA *gca, MRI *mri)
{
  float      x0, y0, z0, fmin, fmax, min_real_val ;
  int        mri_peak, r, max_T1_weighted_image = 0, min_real_bin, peak ;
  float      wm_means[MAX_GCA_INPUTS], tmp[MAX_GCA_INPUTS],
  scales[MAX_GCA_INPUTS], max_wm/*, scale*/ ;
  HISTOGRAM *h_mri, *h_smooth, *h_gca ;
  MRI_REGION box ;
  MRI        *mri_frame, *mri_mask ;

  float      gm_means[MAX_GCA_INPUTS], gray_white_CNR;

  GCAlabelMean(gca, Left_Cerebral_White_Matter, wm_means) ;
  //GCAlabelMean(gca, Left_Cerebral_White_Matter, tmp) ;
  GCAlabelMean(gca, Right_Cerebral_White_Matter, tmp) ;

  // the following rule some times fails for BIRN data, so changed it
  // on 08-01-05 by xhan
#if 0
  max_wm = 0 ;
  for (r = 0 ; r < gca->ninputs ; r++)
  {
    wm_means[r] = (wm_means[r] + tmp[r]) / 2 ;
    if (wm_means[r] > max_wm)
    {
      max_T1_weighted_image = r ;
      max_wm = wm_means[r] ;
    }
  }
#else
  GCAlabelMean(gca, Left_Cerebral_Cortex, gm_means) ;
  for (r = 0 ; r < gca->ninputs ; r++)
  {
    // use modes instead of means
    h_gca = gcaGetLabelHistogram(gca, Left_Cerebral_White_Matter, r) ;
    peak = HISTOfindHighestPeakInRegion(h_gca, 0, h_gca->nbins) ;
    printf("resetting wm mean[%d]: %2.0f --> %2.0f\n",
           r, wm_means[r], h_gca->bins[peak]) ;
    wm_means[r] = h_gca->bins[peak] ;
    if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
      HISTOplot(h_gca, "wm.plt") ;
    HISTOfree(&h_gca) ;

    h_gca = gcaGetLabelHistogram(gca, Left_Cerebral_Cortex, r) ;
    peak = HISTOfindHighestPeakInRegion(h_gca, 0, h_gca->nbins) ;
    gm_means[r] = h_gca->bins[peak] ;
    printf("resetting gm mean[%d]: %2.0f --> %2.0f\n",
           r, gm_means[r], h_gca->bins[peak]) ;
    if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
      HISTOplot(h_gca, "gm.plt") ;
    HISTOfree(&h_gca) ;
  }
  gray_white_CNR = wm_means[0] - gm_means[0];
  for (r = 0 ; r < gca->ninputs ; r++)
  {
    //                wm_means[r] = (wm_means[r] + tmp[r]) / 2 ;
    if ((wm_means[r] - gm_means[r]) > gray_white_CNR)
    {
      max_T1_weighted_image = r ;
      gray_white_CNR = (wm_means[r] - gm_means[r]);
    }
  }
#endif
  printf("input volume #%d is the most T1-like\n",
         max_T1_weighted_image +1);


  max_wm = wm_means[max_T1_weighted_image];

  mri_frame = MRIcopyFrame(mri, NULL, max_T1_weighted_image, 0) ;
  MRIvalRange(mri_frame, &fmin, &fmax) ;
  h_mri = MRIhistogram(mri_frame, nint(fmax-fmin+1)) ;
  HISTOclearZeroBin(h_mri) ; /* ignore background */
  h_smooth = HISTOsmooth(h_mri, NULL, 2) ;
#if 0
  mri_peak = HISTOfindHighestPeakInRegion(h_smooth, 0, h_smooth->nbins/3) ;
#else
  mri_peak = HISTOfindFirstPeak(h_smooth, 5, .1) ;
#endif
  min_real_bin = HISTOfindEndOfPeak(h_smooth, mri_peak, .25) ;
  min_real_val = h_smooth->bins[min_real_bin] ;
  if (min_real_val > 0.25*fmax)   /* for skull-stripped images */
    min_real_val = 0.25*fmax ;
  printf("using real data threshold=%2.1f\n", min_real_val) ;

  MRIfindApproximateSkullBoundingBox(mri_frame, min_real_val, &box) ;
  mri_mask = MRIbinarize(mri_frame, NULL, min_real_val, 0, 1) ;
  MRIfree(&mri_frame) ;
  HISTOfree(&h_mri) ;
  HISTOfree(&h_smooth) ;

  //why divided by 3 for x and y?? mistake or experience?? -xh
  x0 = box.x+box.dx/3 ;
  y0 = box.y+box.dy/3 ;
  z0 = box.z+box.dz/2 ;
  printf("using (%.0f, %.0f, %.0f) as brain centroid...\n",x0, y0, z0) ;
#if 0
  box.x = x0 - HALF_BOX*mri->xsize ;
  box.dx = BOX_SIZE*mri->xsize ;
  box.y = y0 - HALF_BOX*mri->ysize ;
  box.dy = BOX_SIZE*mri->ysize ;
  box.z = z0 - HALF_BOX*mri->zsize ;
  box.dz = BOX_SIZE*mri->zsize ;
#else
  box.dx /= 4 ;
  box.x = x0 - box.dx/2;
  box.dy /= 4 ;
  box.y = y0 - box.dy/2;
  box.dz /= 4 ;
  box.z = z0 - box.dz/2;
#endif
  for (r = 0 ; r < gca->ninputs ; r++)
  {
    mri_frame = MRIcopyFrame(mri, NULL, r, 0) ;
    MRImask(mri_frame, mri_mask, mri_frame, 0,0) ;  /* remove stuff that is 
                                                       background or csf */

    printf("mean wm in atlas = %2.0f, using box (%d,%d,%d) --> (%d, %d,%d) "
           "to find MRI wm\n", wm_means[r], box.x, box.y, box.z,
           box.x+box.dx-1,box.y+box.dy-1, box.z+box.dz-1) ;

    h_mri = MRIhistogramRegion(mri_frame, 0, NULL, &box) ;
    if (gca->ninputs == 1)
      HISTOclearBins(h_mri, h_mri, 0, min_real_val) ;
    if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
      HISTOplot(h_mri, "mri.histo") ;
    HISTOclearZeroBin(h_mri) ;
    if (gca->ninputs == 1)   /* assume it is T1-weighted */
      mri_peak = HISTOfindLastPeak(h_mri, 2*HISTO_WINDOW_SIZE,MIN_HISTO_PCT);
    else
      mri_peak = HISTOfindHighestPeakInRegion(h_mri, 1, h_mri->nbins);
    mri_peak = h_mri->bins[mri_peak] ;
    printf("before smoothing, mri peak at %d\n", mri_peak) ;
    h_smooth = HISTOsmooth(h_mri, NULL, 2) ;
    if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
      HISTOplot(h_smooth, "mri_smooth.histo") ;
    /* assume it is the right-most peak of image
       is supposed to be T1-weighted */
    if (gca->ninputs == 1 &&
        (gca->type == GCA_UNKNOWN || gca->type == GCA_NORMAL ||
         (gca->type == GCA_FLASH && (DEGREES(mri->flip_angle)>15))))
      mri_peak =
        HISTOfindLastPeak(h_smooth, HISTO_WINDOW_SIZE,MIN_HISTO_PCT);
    else
      mri_peak = HISTOfindHighestPeakInRegion(h_smooth, 1, h_mri->nbins);
    mri_peak = h_smooth->bins[mri_peak] ;
    printf("after smoothing, mri peak at %d, scaling input intensities "
           "by %2.3f\n", mri_peak, wm_means[r]/mri_peak) ;
#if 0
    MRIscalarMul(mri_frame, mri_frame, wm_means[r]/mri_peak) ;
    MRIcopyFrame(mri_frame, mri, 0, r) ;
    /* put it back in multi-frame image */
#endif
    if (mri_peak == 0)
      ErrorExit(ERROR_BADPARM,
                "GCAhistoScaleImageIntensities: could not find wm peak") ;
    scales[r] = wm_means[r]/mri_peak ;

    MRIfree(&mri_frame) ;
    HISTOfree(&h_mri) ;
    HISTOfree(&h_smooth) ;

  }

#if 0
  for (scale = 0.0, r = 0 ; r < gca->ninputs ; r++)
    scale += scales[r] ;
  scale /= (double)gca->ninputs ;
  printf("scaling all frames by %2.3f\n", scale) ;
  MRIscalarMul(mri, mri, scale) ;
#else
  // scale each frame independently -xhan
  for (r = 0 ; r < gca->ninputs ; r++)
  {
    printf("scaling channel %d by %g\n", r, scales[r]);
    MRIscalarMulFrame(mri, mri, scales[r], r);
  }
#endif
  MRIfree(&mri_mask) ;
  return(NO_ERROR) ;
}

int
GCAhisto(GCA *gca, int nbins, int **pcounts)
{
  int   *counts, x, y, z ;
  GCA_NODE *gcan ;

  *pcounts = counts = (int *)calloc(nbins+1, sizeof(int)) ;

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        if (gcan->nlabels == 0 || (gcan->nlabels == 1 &&
                                   GCAfindGC(gca,x,y,z,Unknown) != NULL))
          continue ;
        counts[gcan->nlabels]++ ;
      }
    }
  }

  return(NO_ERROR) ;
}

int
GCArenormalizeToExample(GCA *gca, MRI *mri_seg, MRI *mri_T1)
{
  float     intensities[MAX_CMA_LABEL+1] ;
  int       x, y, z, label, labels[MAX_CMA_LABEL+1], width, height, depth,
  counts[MAX_CMA_LABEL+1] ;

  for (label = 0 ; label <= MAX_CMA_LABEL ; label++)
    labels[label] = label ;
  memset(intensities, 0, MAX_CMA_LABEL*sizeof(float)) ;
  memset(counts, 0, MAX_CMA_LABEL*sizeof(int)) ;

  width = mri_seg->width ;
  height = mri_seg->height ;
  depth = mri_seg->height;
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        label = nint(MRIgetVoxVal(mri_seg, x, y, z, 0)) ;
        if (label == Gdiag_no)
          DiagBreak() ;
        if (label > MAX_CMA_LABEL)
        {
          ErrorPrintf(ERROR_BADPARM,
                      "GCArenormalizeToExample: bad label %d", label) ;
          continue ;
        }
        intensities[label] += MRIgetVoxVal(mri_T1, x, y, z, 0) ;
        counts[label]++ ;
      }
    }
  }

  for (label = 0 ; label <= MAX_CMA_LABEL ; label++)
  {
    if (counts[label] <= 0)
      continue ;
    intensities[label] /= (float)counts[label] ;
  }
  GCArenormalizeIntensities(gca, labels, intensities, MAX_CMA_LABEL) ;

  return(NO_ERROR) ;
}

static GC1D *
findGCInWindow(GCA *gca, int x0, int y0, int z0, int label, int wsize)
{
  GC1D       *gc, *gc_min ;
  int        x, y, z, n, whalf ;
  double     dist, min_dist ;
  GCA_NODE   *gcan ;

  min_dist = gca->node_width+gca->node_height+gca->node_depth ;
  gc_min = NULL ;
  whalf = (wsize-1)/2 ;
  for (x = x0-whalf ; x <= x0+whalf ; x++)
  {
    if (x < 0 || x >= gca->node_width)
      continue ;
    for (y = y0-whalf ; y <= y0+whalf ; y++)
    {
      if (y < 0 || y >= gca->node_height)
        continue ;
      for (z = z0-whalf ; z <= z0+whalf  ; z++)
      {
        if (z < 0 || z >= gca->node_depth)
          continue ;
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels && min_dist > 1 ; n++)
        {
          if (gcan->labels[n] != label)
            continue ;
          gc = &gcan->gcs[n] ;
          dist = sqrt(SQR(x-x0)+SQR(y-y0)+SQR(z-z0)) ;
          if (dist < min_dist)
          {
            gc_min = gc ;
            min_dist = dist ;
          }
        }
      }
    }
  }
  return(gc_min) ;
}

static GC1D *
findClosestValidGC(GCA *gca, int x0, int y0, int z0, int label, int check_var)
{
  GC1D       *gc, *gc_min ;
  int        x, y, z, n, wsize ;
  double     dist, min_dist, det ;
  GCA_NODE   *gcan ;
  MATRIX     *m_cov_inv ;

  min_dist = gca->node_width+gca->node_height+gca->node_depth ;
  wsize = 1 ;
  gc_min = NULL ;
  do
  {
    for (x = x0-wsize ; x <= x0+wsize ; x++)
    {
      if (x < 0 || x >= gca->node_width)
        continue ;
      for (y = y0-wsize ; y <= y0+wsize ; y++)
      {
        if (y < 0 || y >= gca->node_height)
          continue ;
        for (z = z0-wsize ; z <= z0+wsize  ; z++)
        {
          if (z < 0 || z >= gca->node_depth)
            continue ;
          dist = sqrt(SQR(x-x0)+SQR(y-y0)+SQR(z-z0)) ;
          if (dist < wsize-2)
            continue ;  // already checked

          gcan = &gca->nodes[x][y][z] ;
          for (n = 0 ; n < gcan->nlabels ; n++)
          {
            if (gcan->labels[n] != label)
              continue ;
            gc = &gcan->gcs[n] ;
            det = covariance_determinant(gc, gca->ninputs) ;
            m_cov_inv =
              load_inverse_covariance_matrix(gc, NULL, gca->ninputs) ;
            if (m_cov_inv == NULL)
              det = -1 ;
            else
              MatrixFree(&m_cov_inv) ;
            if ((check_var && det <= MIN_DET) || gc->ntraining == 0)
              continue ;
            if (dist < min_dist)
            {
              gc_min = gc ;
              min_dist = dist ;
              if (FEQUAL(dist, 1.0))
                return(gc_min) ;
            }
          }
        }
      }
    }
    wsize += 2 ;   // search next ring
		if (wsize > MAX(MAX(gca->node_width, gca->node_height),gca->node_depth))
			break ;
  } while (gc_min == NULL) ;

  if (gc_min)   /* found one in immediate nbhd */
    return(gc_min) ;

  /* couldn't find one close - search everywhere */
  for (x = 0 ; x < gca->node_width && min_dist > 1 ; x++)
  {
    for (y = 0 ; y < gca->node_height && min_dist > 1 ; y++)
    {
      for (z = 0 ; z < gca->node_depth && min_dist > 1 ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels && min_dist > 1 ; n++)
        {
          if (gcan->labels[n] != label)
            continue ;
          gc = &gcan->gcs[n] ;
          det = covariance_determinant(gc, gca->ninputs) ;
          if ((check_var && det <= 0) || gc->ntraining == 0)
            continue ;
          dist = sqrt(SQR(x-x0)+SQR(y-y0)+SQR(z-z0)) ;
          if (dist < min_dist)
          {
            gc_min = gc ;
            min_dist = dist ;
          }
        }
      }
    }
  }
  return(gc_min) ;
}

static int
gcaCheck(GCA *gca)
{
  int x, y, z, ret = NO_ERROR, n, r, c, v ;
  GCA_NODE *gcan ;
  GC1D     *gc ;

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          gc = &gcan->gcs[n] ;
          for (v = r = 0 ; r < gca->ninputs ; r++)
          {
            for (c = r ; c < gca->ninputs ; c++, v++)
              if (!finite(gc->means[r]) || !finite(gc->covars[v]))
              {
                ret = ERROR_BADPARM ;
                DiagBreak() ;
              }
          }
        }
      }
    }
  }
  return(ret) ;
}
int
GCAcomputeVoxelLikelihoods(GCA *gca,
                           MRI *mri_in,
                           int x, int y, int z,
                           TRANSFORM *transform,
                           int *labels, double *likelihoods)
{
  GCA_NODE  *gcan ;
  int       xn, yn, zn, n ;
  float     vals[MAX_GCA_INPUTS] ;

  if (!GCAsourceVoxelToNode(gca, mri_in, transform, x, y, z, &xn, &yn, &zn))
  {
    load_vals(mri_in, x, y, z, vals, gca->ninputs) ;
    gcan = &gca->nodes[xn][yn][zn] ;
    for (n = 0 ; n < gcan->nlabels ; n++)
    {
      labels[n] = gcan->labels[n] ;
      likelihoods[n] =
        GCAcomputeConditionalDensity(&gcan->gcs[n],vals,
                                     gca->ninputs, labels[n]);
    }
    return(gcan->nlabels) ;
  }
  else
    return 0;
}

int
GCAmaxLikelihoodLabel(GCA_NODE *gcan,
                      float *vals, int ninputs, float *plikelihood)
{
  double p, max_p ;
  int    n, best_label ;

  if (gcan->nlabels == 0)
    return(ERROR_BADPARM) ;

  best_label = gcan->labels[0] ;
  max_p = GCAcomputeConditionalDensity(&gcan->gcs[0],vals,
                                       ninputs, gcan->labels[0]) ;

  for (n = 1 ; n < gcan->nlabels ; n++)
  {
    p = GCAcomputeConditionalDensity(&gcan->gcs[n],vals,
                                     ninputs, gcan->labels[n]) ;
    if (p >= max_p)
    {
      max_p = p ;
      best_label = gcan->labels[n] ;
    }
  }
  if (plikelihood)
    *plikelihood = max_p ;
  return(best_label) ;
}
double
GCAcomputeConditionalDensity(GC1D *gc, float *vals, int ninputs, int label)
{
  double  p, dist ;

  /* compute 1-d Mahalanobis distance */
#if 0
  if (label == Unknown)  /* uniform distribution */
  {
    p = 1.0/256.0f ;
  }
  else   /* Gaussian distribution */
#endif
  {
    dist = GCAmahDist(gc, vals, ninputs) ;
    p = (1.0 / (pow(2*M_PI,ninputs/2.0)*sqrt(covariance_determinant
                (gc, ninputs)))) *
        exp(-0.5*dist) ;
  }
  return(p) ;
}
double
GCAcomputeNormalizedConditionalDensity(GCA *gca,
                                       int xp, int yp, int zp,
                                       float *vals, int label)
{
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  int        n ;
  double      p, total, plabel ;

  gcap = &gca->priors[xp][yp][zp] ;
  if (gcap==NULL)
    return(1.0);
  if (gcap->nlabels <= 1)
    return(1.0) ;

  for (plabel = total = 0.0, n = 0 ; n < gcap->nlabels ; n++)
  {
    gc = GCAfindPriorGC(gca, xp, yp, zp, gcap->labels[n]) ;
    if (!gc)
      continue ;
    p = GCAcomputeConditionalDensity(gc,vals,gca->ninputs,gcap->labels[n]);
    if (gcap->labels[n] == label)
      plabel = p ;
    total += p ;
  }

  if (!FZERO(total))
    plabel /= total ;
  return(plabel) ;
}

static double
gcaComputeLogDensity(GC1D *gc, float *vals, int ninputs,
                     float prior, int label)
{
  double log_p ;

  log_p = GCAcomputeConditionalLogDensity(gc, vals, ninputs, label) ;
  log_p += log(prior) ;
  return(log_p) ;
}

double
GCAcomputeConditionalLogDensity(GC1D *gc, float *vals, int ninputs, int label)
{
  double  log_p, det ;


  /* compute 1-d Mahalanobis distance */
#if 0
  if (label == Unknown)  /* uniform distribution */
  {
    log_p = log(1.0/256.0f) ;
  }
  else   /* Gaussian distribution */
#endif
  {
    det = covariance_determinant(gc, ninputs) ;
    log_p = -log(sqrt(det)) - .5*GCAmahDist(gc, vals, ninputs) ;
  }
  return(log_p) ;
}

GCA_SAMPLE *
GCAfindAllSamples(GCA *gca, int *pnsamples, int *exclude_list,
                  int unknown_nbr_spacing)
{
  GCA_SAMPLE *gcas ;
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  int        x, y, z, width, height, depth, n, label, i,
  max_n, max_label, nsamples, r, c, v ;
  float      max_p, prior ;
  int        badcount=0;

  // going through gca->prior volume
  width = gca->prior_width ;
  height = gca->prior_height ;
  depth = gca->prior_depth ;


  /////////////////////////////////////////////////////////////////////////
  // just because of C, you have to repeat the loop twice :-(.....
  // first to find the size needed, second time it gets the info
  ////////////////////////////////////////////////////////////////////////
  /* find the # of places in the atlas that the highest prior class is
     other than unknown.
  */
  //////////// first loop of finding counts //////////////////////////////
  for (nsamples = x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        gcap = &gca->priors[x][y][z] ;
        if (gcap==NULL)
          continue;
        max_p = 0 ;
        max_n = -1 ;
        max_label = 0 ;
        // if no label, ignore
        if (gcap->nlabels == 0)
          continue ;

        //////////////// debug code /////////////////////////////////
        if (x*gca->prior_spacing == Gx && y*gca->prior_spacing == Gy &&
            z*gca->prior_spacing == Gz)
          DiagBreak() ;
        /////////////////////////////////////////////////////////////

        // go through the labels to find the max value
        // for prior probablity
        // and its label
        for (n = 0 ; n < gcap->nlabels ; n++)
        {
          label = gcap->labels[n] ;
          prior = gcap->priors[n] ;
          if (prior >= max_p)
          {
            max_n = n ;
            max_p = prior ;
            max_label = gcap->labels[n] ;
          }
        }
        // if this label is in the exclude list, continue
        if (exclude_list && exclude_list[max_label] > 0)
          continue ;
        // if the label is unknown and no +/-1
        // neighbor has different label, continue
        if (IS_UNKNOWN(max_label) &&
            (different_nbr_max_labels(gca, x, y, z,
                                      unknown_nbr_spacing, 0) == 0))
          continue ;

        nsamples++ ;
      }
    }
  }
  ///////////////////////////////////////////////////////////////////////


  // allocate nsamples worth GCA_SAMPLE
  // samples are non-unknowns and unknown with neighbor not unknown
  gcas = calloc(nsamples, sizeof(GCA_SAMPLE )) ;

  badcount = 0;
  // get the values for gcas
  // repeat the same thing again so that we can add info
  ////////////////// second loop of adding info ////////////////////////
  for (i = x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        gcap = &gca->priors[x][y][z] ;
        if (gcap==NULL)
          continue;
        max_p = 0 ;
        max_n = -1 ;
        max_label = 0 ;
        // no label, ignore
        if (gcap->nlabels == 0)
          continue ;

        for (n = 0 ; n < gcap->nlabels ; n++)
        {
          label = gcap->labels[n] ;
          prior = gcap->priors[n] ;
          if (prior >= max_p)
          {
            max_n = n ;
            max_p = prior ;
            max_label = gcap->labels[n] ;
          }
        }
        if (exclude_list && exclude_list[max_label] > 0)
          continue ;
        if (IS_UNKNOWN(max_label) &&
            (different_nbr_max_labels(gca, x, y, z,
                                      unknown_nbr_spacing, 0) == 0))
          continue ;

        // store prior coordinates
        gcas[i].xp = x ;
        gcas[i].yp = y ;
        gcas[i].zp = z ;
        //////////////////////////////////////
        // store talarached coordinate
        // talarached and prior has the same direction cosines
        // thus prior->talarached is only the scale difference
        // Note that we pick only one point in prior_spacing, though.
        gcas[i].x = x*gca->prior_spacing/gca->xsize ;
        gcas[i].y = y*gca->prior_spacing/gca->ysize ;
        gcas[i].z = z*gca->prior_spacing/gca->zsize ;
        //////////////////////////////////////
        gcas[i].label = max_label ;
        gcas[i].prior = max_p ;
        gcas[i].means = (float *)calloc(gca->ninputs, sizeof(float)) ;
        gcas[i].covars =
          (float *)calloc((gca->ninputs*(gca->ninputs+1))/2,
                          sizeof(float)) ;
        if (!gcas[i].means || !gcas[i].covars)
          ErrorExit(ERROR_NOMEMORY,
                    "GCAfindAllSamples: could not allocate mean "
                    "(%d) and covariance (%d) matrices",
                    gca->ninputs, gca->ninputs*(gca->ninputs+1)/2) ;
        gc = GCAfindPriorGC(gca, x, y, z, max_label) ;
        if (gc) // found
        {
          for (v = r = 0 ; r < gca->ninputs ; r++)
          {
            gcas[i].means[r] = gc->means[r] ;
            for (c = r ; c < gca->ninputs ; c++, v++)
              gcas[i].covars[v] = gc->covars[v] ;
          }
        }
        else // not found
        {
          badcount++;
          for (v = r = 0 ; r < gca->ninputs ; r++)
          {
            gcas[i].means[r] = 0.0 ;
            for (c = r ; c < gca->ninputs ; c++, v++)
            {
              if (c == r)
                gcas[i].covars[v] = 1.0 ;
              else
                gcas[i].covars[v] = 0.0 ;
            }
          }
        }
        gcas[i].log_p = 0 ; // initialize
        i++ ;
      }
    }
  }
  if (badcount > 0)
  {
    fprintf(stdout, "**************************************\n");
    fprintf(stdout, "those with gc cannot be found = %d\n", badcount);
    fprintf(stdout, "**************************************\n");
  }
  *pnsamples = nsamples ;
  return(gcas) ;
}
int
GCAcomputeMAPlabelAtLocation(GCA *gca, int xp, int yp, int zp, float *vals,
                             int *pmax_n, float *plog_p)
{
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  int        n, max_n, max_label ;
  float      log_p, max_log_p ;

  gcap = &gca->priors[xp][yp][zp] ;
  if (gcap==NULL)
    return (Unknown);
  if (gcap->nlabels == 0)
  {
    if (plog_p)
      *plog_p = 0.0 ;
    if (pmax_n)
      *pmax_n = -1 ;
    return(Unknown) ;
  }

  // start from label[0]
  max_label = gcap->labels[0] ;
  max_n = 0 ;
  gc = GCAfindPriorGC(gca, xp, yp, zp, gcap->labels[0]) ;
  if (gc)
    max_log_p = gcaComputeLogDensity(gc, vals,
                                     gca->ninputs,
                                     gcap->priors[0],
                                     max_label) ;
  else
    max_log_p = -100000 ;
  // go through all the labels here
  for (n = 1 ; n < gcap->nlabels ; n++)
  {
    gc = GCAfindPriorGC(gca, xp, yp, zp, gcap->labels[n]) ;
    if (!gc)
      continue ;
    // calculate log_p
    log_p = gcaComputeLogDensity(gc,vals,
                                 gca->ninputs,
                                 gcap->labels[n],
                                 gcap->priors[n]);
    if (log_p > max_log_p)
    {
      max_log_p = log_p ;
      max_n = n ;
      max_label = gcap->labels[n] ;
    }
  }

  if (plog_p)
    *plog_p = max_log_p ;
  if (pmax_n)
    *pmax_n = max_n ;
  return(max_label) ;// return most probable label
}
int
GCAcomputeMLElabelAtLocation(GCA *gca, int xp, int yp, int zp, float *vals,
                             int *pmax_n, float *plog_p)
{
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  int        n, max_n, max_label ;
  float      log_p, max_log_p ;

  gcap = &gca->priors[xp][yp][zp] ;
  if (gcap == NULL || gcap->nlabels <= 0)
    return(Unknown);
  if (gcap->nlabels == 0)
  {
    if (plog_p)
      *plog_p = 0.0 ;
    if (pmax_n)
      *pmax_n = -1 ;
    return(Unknown) ;
  }

  max_label = gcap->labels[0] ;
  max_n = 0 ;
  gc = GCAfindPriorGC(gca, xp, yp, zp, gcap->labels[0]) ;
  if (gc)
    max_log_p = GCAcomputeConditionalLogDensity(gc, vals,
                gca->ninputs, max_label) ;
  else
    max_log_p = -100000 ;
  for (n = 1 ; n < gcap->nlabels ; n++)
  {
    gc = GCAfindPriorGC(gca, xp, yp, zp, gcap->labels[n]) ;
    if (!gc)
      continue ;
    log_p = GCAcomputeConditionalLogDensity(gc,vals,gca->ninputs,
                                            gcap->labels[n]);
    if (log_p > max_log_p)
    {
      max_log_p = log_p ;
      max_n = n ;
      max_label = gcap->labels[n] ;
    }
  }

  if (plog_p)
    *plog_p = max_log_p ;
  if (pmax_n)
    *pmax_n = max_n ;
  return(max_label) ;
}
GC1D *
alloc_gcs(int nlabels, int flags, int ninputs)
{
  GC1D  *gcs ;
  int   i ;

  gcs = (GC1D *)calloc(nlabels, sizeof(GC1D)) ;
  if (gcs == NULL)
    ErrorExit(ERROR_NOMEMORY, "alloc_gcs(%d, %x): could not allocated %d gcs",
              nlabels, flags, nlabels) ;
  for (i = 0 ; i < nlabels ; i++)
  {
    gcs[i].means = (float *)calloc(ninputs, sizeof(float)) ;
    gcs[i].covars = (float *)calloc((ninputs*(ninputs+1))/2, sizeof(float)) ;
    if (!gcs[i].means || !gcs[i].covars)
      ErrorExit(ERROR_NOMEMORY, "could not allocate mean (%d) "
                "and covariance (%d) matrices",
                ninputs, ninputs*(ninputs+1)/2) ;
  }
  if (flags & GCA_NO_MRF)
    return(gcs) ;

  for (i = 0 ; i < nlabels ; i++)
  {
    gcs[i].nlabels = (short *)calloc(GIBBS_NEIGHBORHOOD, sizeof(short)) ;
    gcs[i].labels =
      (unsigned short **)calloc(GIBBS_NEIGHBORHOOD, sizeof(unsigned short *)) ;
    gcs[i].label_priors =
      (float **)calloc(GIBBS_NEIGHBORHOOD, sizeof(float *)) ;
    if (!gcs[i].nlabels || !gcs[i].labels || !gcs[i].label_priors)
      ErrorExit(ERROR_NOMEMORY,
                "alloc_gcs(%d, %x): could not allocated %d gcs(%d)",
                nlabels, flags, nlabels, i) ;
  }
  return(gcs) ;
}

int
free_gcs(GC1D *gcs, int nlabels, int ninputs)
{
  int   i, j ;

  for (i = 0 ; i < nlabels ; i++)
  {
    if (gcs[i].means)
      free(gcs[i].means) ;
    if (gcs[i].covars)
      free(gcs[i].covars) ;
    if (gcs[i].nlabels)  /* gibbs stuff allocated */
    {
      for (j = 0 ; j < GIBBS_NEIGHBORHOOD ; j++)
      {
        if (gcs[i].labels[j])
          free(gcs[i].labels[j]) ;
        if (gcs[i].label_priors[j])
          free(gcs[i].label_priors[j]) ;
      }
      free(gcs[i].nlabels) ;
      free(gcs[i].labels) ;
      free(gcs[i].label_priors) ;
    }
  }

  free(gcs) ;
  return(NO_ERROR) ;
}

int
copy_gcs(int nlabels, GC1D *gcs_src, GC1D *gcs_dst, int ninputs)
{
  int   i, j, k, r, c,v ;

  for (i = 0 ; i < nlabels ; i++)
  {
    for (v = r = 0 ; r < ninputs ; r++)
    {
      gcs_dst[i].means[r] = gcs_src[i].means[r] ;
      for (c = r ; c < ninputs ; c++, v++)
        gcs_dst[i].covars[v] = gcs_src[i].covars[v] ;
    }
    gcs_dst[i].ntraining = gcs_src[i].ntraining ;
    if (gcs_dst[i].nlabels == NULL)   /* NO_MRF flag must be set */
      continue ;
    for (j = 0 ; j < GIBBS_NEIGHBORHOOD ; j++)
    {
      gcs_dst[i].nlabels[j] = gcs_src[i].nlabels[j] ;
      if (!gcs_dst[i].label_priors[j])
      {
        gcs_dst[i].label_priors[j] =
          (float *)calloc(gcs_src[i].nlabels[j],sizeof(float)) ;
        gcs_dst[i].labels[j] =
          (unsigned short *)calloc(gcs_src[i].nlabels[j],
                                  sizeof(unsigned short)) ;
        if (!gcs_dst[i].label_priors[j] || !gcs_dst[i].labels[j])
          ErrorExit(ERROR_NOMEMORY,
                    "copy_gcs(%d): i=%d, j=%d, could not gibbs\n",
                    nlabels, i, j) ;
      }
      for (k = 0 ; k < gcs_src[i].nlabels[j] ; k++)
      {
        gcs_dst[i].label_priors[j][k] = gcs_src[i].label_priors[j][k] ;
        gcs_dst[i].labels[j][k] = gcs_src[i].labels[j][k] ;
      }
    }
  }
  return(NO_ERROR) ;
}

int
GCAfreeGibbs(GCA *gca)
{
  int       x, y, z,n, i ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;

  if (gca->flags & GCA_NO_MRF)
    return(NO_ERROR) ;  /* already done */

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          gc = &gcan->gcs[n] ;
          for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
          {
            free(gc->label_priors[i]) ;
            free(gc->labels[i]) ;
            gc->label_priors[i] = NULL ;
            gc->labels[i] = NULL ;
          }
          free(gc->nlabels) ;
          free(gc->labels) ;
          free(gc->label_priors) ;
          gc->nlabels = NULL ;
          gc->labels = NULL ;
          gc->label_priors = NULL ;
        }
      }
    }
  }
  gca->flags |= GCA_NO_MRF ;
  return(NO_ERROR) ;
}

int
GCAcomputeSampleCoords(GCA *gca, MRI *mri, GCA_SAMPLE *gcas,
                       int nsamples,TRANSFORM *transform)
{
  int    n, x, y, z ;

  TransformInvert(transform, mri) ;
  if (transform->type == LINEAR_VOX_TO_VOX)
  {
    LTA *lta;
    lta = (LTA *) transform->xform;
    fprintf(stdout, "INFO: compute sample coordinates transform\n");
    MatrixPrint(stdout, lta->xforms[0].m_L);
  }
  fprintf(stdout, "INFO: transform used\n");

  for (n = 0 ; n < nsamples ; n++)
  {
    if (gcas[n].label == Gdiag_no)
      DiagBreak() ;
    if (!GCApriorToSourceVoxel(gca, mri, transform,
                               gcas[n].xp, gcas[n].yp, gcas[n].zp,
                               &x, &y, &z))
    {
      gcas[n].x = x ;
      gcas[n].y = y ;
      gcas[n].z = z ;
      if (DIAG_VERBOSE_ON && Gdiag & DIAG_SHOW)
        printf("label %d: (%d, %d, %d) <-- (%d, %d, %d)\n",
               gcas[n].label,gcas[n].xp,gcas[n].yp,gcas[n].zp, x, y, z) ;
    }
  }
  return(NO_ERROR) ;
}

static HISTOGRAM *
gcaHistogramSamples(GCA *gca, GCA_SAMPLE *gcas, MRI *mri,
                    TRANSFORM *transform, int nsamples,
                    HISTOGRAM *histo, int frame)
{
  int    i ;
  double mean, var ;
  Real   val ;
  float  fmin, fmax ;

  if (!histo)
  {
    MRIvalRangeFrame(mri, &fmin, &fmax, frame) ;
    histo = HISTOalloc(nint(fmax-fmin+1)) ;
  }
  else
    HISTOclear(histo, histo) ;

  for (mean = var = 0.0, i = 0 ; i < nsamples ; i++)
  {
    MRIsampleVolumeFrame(mri, gcas[i].x,gcas[i].y,gcas[i].z, frame, &val) ;
    histo->counts[(int)val]++ ;
    mean += val ;
    var += (val*val) ;
  }

  mean /= (double)nsamples ;
  var = var / (double)nsamples - mean*mean ;
  return(histo) ;
}
int
GCArenormalizeFromAtlas(GCA *gca, GCA *gca_template)
{
  int   xs, ys, zs, xt, yt, zt, ns, label, v ;
  float scale ;
  GCA_NODE *gcan ;
  GC1D      *gc, *gct ;

  scale = (float)gca_template->node_width / (float)gca->node_width ;

  for (xs = 0 ; xs < gca->node_width ; xs++)
  {
    xt = (int)(scale*xs) ;
    for (ys = 0 ; ys < gca->node_width ; ys++)
    {
      yt = (int)(scale*ys) ;
      for (zs = 0 ; zs < gca->node_width ; zs++)
      {
        zt = (int)(scale*zs) ;
        if (xs == Ggca_x && ys == Ggca_y && zs == Ggca_z)
          DiagBreak() ;
        if (xt == Ggca_x && yt == Ggca_y && zt == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[xs][ys][zs] ;
        for (ns = 0 ; ns < gcan->nlabels ; ns++)
        {
          label = gcan->labels[ns] ;
          gc = &gcan->gcs[ns] ;
          gct = GCAfindGC(gca_template, xt, yt, zt, label) ;
          if (gct == NULL)
            continue ;    /* label not in template GCA */
          for (v = 0 ; v < gca->ninputs ; v++)
            gc->means[v] = gct->means[v] ;
        }
      }
    }
  }
  return(NO_ERROR) ;
}


void
load_vals(MRI *mri_inputs, float x, float y, float z, float *vals, int ninputs)
{
  int  n ;
  Real val ;

  // go through all inputs and get values from inputs
  for (n = 0 ; n < ninputs ; n++)
  {
    // trilinear value at float x, y, z
    MRIsampleVolumeFrame(mri_inputs, x, y, z, n, &val) ;
    vals[n] = val ;
  }
}

double
GCAmahDist(GC1D *gc, float *vals, int ninputs)
{
  static VECTOR *v_means = NULL, *v_vals = NULL ;
  static MATRIX *m_cov = NULL, *m_cov_inv ;
  int    i ;
  double dsq ;

  if (ninputs == 1)
  {
    float  v ;
    v = vals[0] - gc->means[0] ;
    dsq = v*v / gc->covars[0] ;
    return(dsq) ;
  }

  if (v_vals && ninputs != v_vals->rows)
    VectorFree(&v_vals) ;
  if (v_means && ninputs != v_means->rows)
    VectorFree(&v_means) ;
  if (m_cov && (ninputs != m_cov->rows || ninputs != m_cov->cols))
  {
    MatrixFree(&m_cov) ;
    MatrixFree(&m_cov_inv) ;
  }
  v_means = load_mean_vector(gc, v_means, ninputs) ;
  m_cov = load_covariance_matrix(gc, m_cov, ninputs) ;
  if (v_vals == NULL)
    v_vals = VectorClone(v_means) ;
  for (i = 0 ; i < ninputs ; i++)
    VECTOR_ELT(v_vals, i+1) = vals[i] ;

  VectorSubtract(v_means, v_vals, v_vals) ;  /* v_vals now has mean removed */
  m_cov_inv = MatrixInverse(m_cov, m_cov_inv) ;
  if (!m_cov_inv)
    ErrorExit(ERROR_BADPARM, "singular covariance matrix!") ;
  MatrixSVDInverse(m_cov, m_cov_inv) ;

  MatrixMultiply(m_cov_inv, v_vals, v_means) ;
  /* v_means is now inverse(cov) * v_vals */
  dsq = VectorDot(v_vals, v_means) ;

  return(dsq);
}
double
GCAmahDistIdentityCovariance(GC1D *gc, float *vals, int ninputs)
{
  static VECTOR *v_means = NULL, *v_vals = NULL ;
  int    i ;
  double dsq ;

  if (v_vals && ninputs != v_vals->rows)
    VectorFree(&v_vals) ;
  if (v_means && ninputs != v_means->rows)
    VectorFree(&v_means) ;
  v_means = load_mean_vector(gc, v_means, ninputs) ;
  if (v_vals == NULL)
    v_vals = VectorClone(v_means) ;
  for (i = 0 ; i < ninputs ; i++)
    VECTOR_ELT(v_vals, i+1) = vals[i] ;

  VectorSubtract(v_means, v_vals, v_vals) ;  /* v_vals now has mean removed */
  dsq = VectorDot(v_vals, v_vals) ;

  return(dsq);
}

double
GCAcomputePosteriorDensity(GCA_PRIOR *gcap, GCA_NODE *gcan,
                           int n, float *vals, int ninputs)
{
  GC1D *gc ;
  double p ;

  gc = &gcan->gcs[n] ;

  p = GCAcomputeConditionalDensity(gc, vals, ninputs, gcan->labels[n]) ;
  p *= getPrior(gcap, gcan->labels[n]) ;
  return(p) ;
}

VECTOR *
load_mean_vector(GC1D *gc, VECTOR *v_means, int ninputs)
{
  int n ;

  if (v_means == NULL)
    v_means = VectorAlloc(ninputs, MATRIX_REAL) ;

  for (n = 0 ; n < ninputs ; n++)
    VECTOR_ELT(v_means, n+1) = gc->means[n] ;

  return(v_means) ;
}

MATRIX *
load_covariance_matrix(GC1D *gc, MATRIX *m_cov, int ninputs)
{
  int n, m, i ;

  if (m_cov == NULL)
    m_cov = MatrixAlloc(ninputs, ninputs, MATRIX_REAL) ;

  for (i = n = 0 ; n < ninputs ; n++)
    for (m = n ; m < ninputs ; m++, i++)
    {
      *MATRIX_RELT(m_cov, n+1, m+1) = gc->covars[i] ;
      if (m != n)
        *MATRIX_RELT(m_cov, m+1, n+1) = gc->covars[i] ;
    }

  return(m_cov) ;
}

int
set_mean_vector(GC1D *gc, VECTOR *v_means, int ninputs)
{
  int n ;

  for (n = 0 ; n < ninputs ; n++)
    gc->means[n] = VECTOR_ELT(v_means, n+1) ;

  return(NO_ERROR) ;
}

int
set_covariance_matrix(GC1D *gc, MATRIX *m_cov, int ninputs)
{
  int n, m, i ;

  for (i = n = 0 ; n < ninputs ; n++)
    for (m = n ; m < ninputs ; m++, i++)
    {
      gc->covars[i] = *MATRIX_RELT(m_cov, n+1, m+1) ;
      if (m != n)
        gc->covars[i] = *MATRIX_RELT(m_cov, m+1, n+1) ;
    }

  return(NO_ERROR) ;
}

MATRIX *
load_inverse_covariance_matrix(GC1D *gc, MATRIX *m_inv_cov, int ninputs)
{
  static MATRIX *m_cov = NULL ;

  if (m_cov && (m_cov->rows != ninputs || m_cov->cols != ninputs))
    MatrixFree(&m_cov) ;
  m_cov = load_covariance_matrix(gc, m_cov, ninputs) ;
  m_inv_cov = MatrixInverse(m_cov, m_inv_cov) ;
#if 0
  if (m_inv_cov == NULL)
    ErrorPrintf(ERROR_BADPARM,
                "load_inverse_covariance_matrix: "
                "singular covariance matrix!") ;
#endif
  return(m_inv_cov) ;
}

double
covariance_determinant(GC1D *gc, int ninputs)
{
  double det ;
  static MATRIX *m_cov = NULL ;

  if (ninputs == 1)
    return(gc->covars[0]) ;
  if (m_cov && (m_cov->rows != ninputs || m_cov->cols != ninputs))
    MatrixFree(&m_cov) ;
  m_cov = load_covariance_matrix(gc, m_cov, ninputs) ;
  det = MatrixDeterminant(m_cov) ;
#if 0
  if (det <= 0)
    det = 0.001 ;
#endif
  return(det) ;
}

static double
gcaComputeSampleLogDensity(GCA_SAMPLE *gcas, float *vals, int ninputs)
{
  double log_p ;

  log_p = gcaComputeSampleConditionalLogDensity(gcas, vals,
          ninputs, gcas->label) ;
  log_p += log(gcas->prior) ;
  return(log_p) ;
}

static double
gcaComputeSampleConditionalLogDensity(GCA_SAMPLE *gcas,
                                      float *vals,
                                      int ninputs, int label)
{
  double  log_p, det ;


#if 0
  if (label == Unknown)  /* uniform distribution */
  {
    log_p = log(1.0/256.0f) ;
  }
  else   /* Gaussian distribution */
#endif
  {
    det = sample_covariance_determinant(gcas, ninputs) ;
    log_p = -log(sqrt(det)) - .5*GCAsampleMahDist(gcas, vals, ninputs) ;
  }
  return(log_p) ;
}
static VECTOR *
load_sample_mean_vector(GCA_SAMPLE *gcas, VECTOR *v_means, int ninputs)
{
  int n ;

  if (v_means == NULL)
    v_means = VectorAlloc(ninputs, MATRIX_REAL) ;

  for (n = 0 ; n < ninputs ; n++)
    VECTOR_ELT(v_means, n+1) = gcas->means[n] ;

  return(v_means) ;
}
static MATRIX *
load_sample_covariance_matrix(GCA_SAMPLE *gcas, MATRIX *m_cov, int ninputs)
{
  int n, m, i ;

  if (m_cov == NULL)
    m_cov = MatrixAlloc(ninputs, ninputs, MATRIX_REAL) ;

  for (i = n = 0 ; n < ninputs ; n++)
    for (m = n ; m < ninputs ; m++, i++)
    {
      *MATRIX_RELT(m_cov, n+1, m+1) = gcas->covars[i] ;
    }

  return(m_cov) ;
}

static double
sample_covariance_determinant(GCA_SAMPLE *gcas, int ninputs)
{
  double det ;
  static MATRIX *m_cov = NULL ;

  if (ninputs == 1)
    return(gcas->covars[0]) ;
  if (m_cov && (m_cov->rows != ninputs || m_cov->cols != ninputs))
    MatrixFree(&m_cov) ;

  m_cov = load_sample_covariance_matrix(gcas, m_cov, ninputs) ;
  det = MatrixDeterminant(m_cov) ;
  return(det) ;
}
double
GCAsampleMahDist(GCA_SAMPLE *gcas, float *vals, int ninputs)
{
  static VECTOR *v_means = NULL, *v_vals = NULL ;
  static MATRIX *m_cov = NULL, *m_cov_inv ;
  int    i ;
  double dsq ;

  if (ninputs == 1)
  {
    float  v ;
    v = vals[0] - gcas->means[0] ;
    dsq = v*v / gcas->covars[0] ;
    return(dsq) ;
  }

  if (v_vals && ninputs != v_vals->rows)
    VectorFree(&v_vals) ;
  if (v_means && ninputs != v_means->rows)
    VectorFree(&v_means) ;
  if (m_cov && (ninputs != m_cov->rows || ninputs != m_cov->cols))
  {
    MatrixFree(&m_cov) ;
    MatrixFree(&m_cov_inv) ;
  }

  v_means = load_sample_mean_vector(gcas, v_means, ninputs) ;
  m_cov = load_sample_covariance_matrix(gcas, m_cov, ninputs) ;

  if (v_vals == NULL)
    v_vals = VectorClone(v_means) ;
  for (i = 0 ; i < ninputs ; i++)
    VECTOR_ELT(v_vals, i+1) = vals[i] ;

  VectorSubtract(v_means, v_vals, v_vals) ;  /* v_vals now has mean removed */
  m_cov_inv = MatrixInverse(m_cov, m_cov_inv) ;
  if (!m_cov_inv)
    ErrorExit(ERROR_BADPARM, "singular covariance matrix!") ;

  MatrixMultiply(m_cov_inv, v_vals, v_means) ;
  /* v_means is now inverse(cov) * v_vals */
  dsq = VectorDot(v_vals, v_means) ;

  return(dsq);
}
#if 0
static double
gcaComputeSampleConditionalDensity(GCA_SAMPLE *gcas,
                                   float *vals, int ninputs, int label)
{
  double  p, dist ;

  /* compute 1-d Mahalanobis distance */
#if 0
  if (label == Unknown)  /* uniform distribution */
  {
    p = 1.0/256.0f ;
  }
  else   /* Gaussian distribution */
#endif
  {
    dist = GCAsampleMahDist(gcas, vals, ninputs) ;
    p = 1 / sqrt(sample_covariance_determinant(gcas, ninputs)) * exp(-dist) ;
  }
  return(p) ;
}
#endif
int
GCAlabelExists(GCA *gca, MRI *mri, TRANSFORM *transform,
               int x, int y, int z, int label)
{
#if 0
  int  xn, yn, zn ;

  if (!GCAsourceVoxelToNode(gca, mri, transform, x, y, z, &xn, &yn, &zn))
  {
    if (GCAfindGC(gca, xn, yn, zn, label) == NULL)
      return(0) ;
    return(1) ;
  }
  else
    return 0;
#else
  return(GCAisPossible(gca, mri, label, transform, x, y, z,0)) ;
#endif
}

GC1D *
GCAfindSourceGC(GCA *gca, MRI *mri, TRANSFORM *transform,
                int x, int y, int z, int label)
{
  int  xn, yn, zn ;
  GC1D *gc=NULL ;

  if (!GCAsourceVoxelToNode(gca, mri, transform, x, y, z, &xn, &yn, &zn))
  {
    gc = GCAfindGC(gca, xn, yn, zn, label) ;
  }
  return(gc) ;
}
int
GCAfreeSamples(GCA_SAMPLE **pgcas, int nsamples)
{
  GCA_SAMPLE *gcas ;
  int         i ;

  gcas = *pgcas ;
  *pgcas = NULL ;

  if (gcas == NULL)
    return(NO_ERROR) ;

  for (i = 0 ; i < nsamples ; i++)
  {
    if (gcas[i].means)
      free(gcas[i].means) ;
    if (gcas[i].covars)
      free(gcas[i].covars) ;
  }
  free(gcas) ;
  return(NO_ERROR) ;
}
int
GCAnormalizePD(GCA *gca, MRI *mri_inputs, TRANSFORM *transform)
{
  double     mri_PD  = 0.0, gca_PD = 0.0 ;
  int        n, x, y, z, brain_node, nvals, label ;
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  Real      val ;

  if (mri_inputs->nframes != 2 || gca->ninputs != 2)
    ErrorReturn(ERROR_BADPARM,
                (ERROR_BADPARM,
                 "GCAnormalizePD: invalid input size (%d) or gca size (%d)",
                 mri_inputs->nframes, gca->ninputs)) ;

  for (nvals = x = 0 ;  x < mri_inputs->width ; x++)
  {
    for (y = 0 ; y < mri_inputs->height ; y++)
    {
      for (z = 0 ; z < mri_inputs->depth ; z++)
      {
        gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
        if (gcap == NULL || gcap->nlabels <= 0)
          continue;
        for (label = brain_node = 0, n = 0 ; \
             !brain_node && n < gcap->nlabels ; n++)
          if (IS_WM(gcap->labels[n]) && gcap->priors[n] > 0.5)
          {
            brain_node = 1 ;
            label = gcap->labels[n] ;
          }
        if (!brain_node)
          continue ;   /* no valid data here */
        gc = GCAfindSourceGC(gca, mri_inputs, transform,
                             x, y, z, label) ;
        if (gc)
        {
          MRIsampleVolumeFrameType(mri_inputs, x, y, z, 1,
                                   SAMPLE_NEAREST, &val) ;
          nvals++ ;
          mri_PD += val ;
          gca_PD += gc->means[1] ;
        }
      }
    }
  }
  mri_PD /= (double)nvals ;
  gca_PD /= (double)nvals ;
  printf("mean PD in volume %2.1f, mean GCA PD %2.1f - scaling by %2.3f\n",
         mri_PD, gca_PD, gca_PD / mri_PD) ;
  MRIscalarMulFrame(mri_inputs, mri_inputs, gca_PD / mri_PD, 1) ;

  return(NO_ERROR) ;
}
GCA *
GCAcreateWeightedFlashGCAfromParameterGCA(GCA *gca_T1PD,
    double *TR, double *fa, double *TE,
    int nflash, double *wts,
    double lambda)
{
  GCA        *gca_flash ;
  GCA_PRIOR  *gcap_src, *gcap_dst ;
  GCA_NODE   *gcan_src, *gcan_dst ;
  GC1D       *gc_src, *gc_dst ;
  MATRIX     *m_jacobian, *m_cov_src = NULL, *m_cov_dst = NULL, \
                                       *m_jacobian_T = NULL, *m_tmp = NULL ;
  VECTOR     *v_wts, *v_wts_T ;
  int        n, x, y, z, i, j, v, label_count = 0 ;
  double     T1, PD, label_means[MAX_GCA_INPUTS] ;

  if (gca_T1PD->ninputs != 2)
    ErrorExit(ERROR_BADPARM,
              "GCAcreateWeightedFlashGCAfromParameterGCA: "
              "input gca must be T1/PD (ninputs=%d, should be 2",
              gca_T1PD->ninputs) ;
  gca_flash = GCAalloc(nflash, gca_T1PD->prior_spacing, gca_T1PD->node_spacing,
                       gca_T1PD->node_width*gca_T1PD->node_spacing,
                       gca_T1PD->node_height*gca_T1PD->node_spacing,
                       gca_T1PD->node_depth*gca_T1PD->node_spacing,
                       GCA_NO_FLAGS) ;

  m_jacobian =
    MatrixAlloc(gca_flash->ninputs, gca_T1PD->ninputs, MATRIX_REAL) ;
  v_wts = VectorAlloc(nflash, MATRIX_REAL) ;
  for (i = 1 ; i <= nflash ; i++)
    VECTOR_ELT(v_wts,i) = wts[i-1] ;
  v_wts_T = VectorTranspose(v_wts, NULL) ;

  /* first copy over priors */
  for (x = 0 ; x < gca_flash->prior_width ; x++)
  {
    for (y = 0 ; y < gca_flash->prior_height ; y++)
    {
      for (z = 0 ; z < gca_flash->prior_depth ; z++)
      {
        gcap_src = &gca_T1PD->priors[x][y][z] ;
        if (gcap_src==NULL)
          continue;
        gcap_dst = &gca_flash->priors[x][y][z] ;
        if (gcap_dst==NULL)
          continue;
        gcap_dst->nlabels = gcap_src->nlabels ;
        if (gcap_dst==NULL)
          continue;
        if (gcap_src->nlabels > gcap_dst->max_labels)
        {
          free(gcap_dst->priors) ;
          free(gcap_dst->labels) ;

          gcap_dst->labels =
            (unsigned short *)calloc(gcap_src->nlabels,
                                    sizeof(unsigned short)) ;
          if (!gcap_dst->labels)
            ErrorExit(ERROR_NOMEMORY,
                      "GCAcreateWeightedFlashGCAfromParameterGCA:"
                      " couldn't allocate %d labels",
                      gcap_src->nlabels) ;

          gcap_dst->priors =
            (float *)calloc(gcap_src->nlabels, sizeof(float)) ;
          if (!gcap_dst->priors)
            ErrorExit(ERROR_NOMEMORY,
                      "GCAcreateWeightedFlashGCAfromParameterGCA:"
                      " couldn't allocate %d priors",
                      gcap_src->nlabels) ;
          gcap_dst->max_labels = gcap_dst->nlabels ;
        }
        gcap_dst->total_training = gcap_src->total_training ;
        for (n = 0 ; n < gcap_src->nlabels ; n++)
        {
          gcap_dst->labels[n] = gcap_src->labels[n] ;
          gcap_dst->priors[n] = gcap_src->priors[n] ;
        }
      }
    }
  }

  /* now copy over classifiers and Markov stuff, using Jacobian to */
  /* map to new image space */
  for (x = 0 ; x < gca_flash->node_width ; x++)
  {
    for (y = 0 ; y < gca_flash->node_height ; y++)
    {
      for (z = 0 ; z < gca_flash->node_depth ; z++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak()  ;
        gcan_src = &gca_T1PD->nodes[x][y][z] ;
        gcan_dst = &gca_flash->nodes[x][y][z] ;
        gcan_dst->nlabels = gcan_src->nlabels ;
        gcan_dst->total_training = gcan_src->total_training ;
        if (gcan_src->nlabels > gcan_dst->max_labels)
        {
          free(gcan_dst->labels) ;
          free_gcs(gcan_dst->gcs,
                   gcan_dst->max_labels,
                   gca_flash->ninputs) ;

          gcan_dst->labels =
            (unsigned short *)calloc(gcan_src->nlabels,
                                    sizeof(unsigned short)) ;
          if (!gcan_dst->labels)
            ErrorExit(ERROR_NOMEMORY,
                      "GCAcreateWeightedFlashGCAfromParameterGCA:"
                      " couldn't allocate %d labels",
                      gcan_src->nlabels) ;

          gcan_dst->gcs = alloc_gcs(gcan_src->nlabels,
                                    GCA_NO_FLAGS, nflash) ;
          gcan_dst->max_labels = gcan_dst->nlabels ;
        }
        for (n = 0 ; n < gcan_src->nlabels ; n++)
        {
          gcan_dst->labels[n] = gcan_src->labels[n] ;
          gc_src = &gcan_src->gcs[n] ;
          gc_dst = &gcan_dst->gcs[n] ;
          gc_dst->ntraining = gc_src->ntraining ;
          gc_dst->n_just_priors = gc_src->n_just_priors ;
          gc_dst->regularized = gc_src->regularized ;
          for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
          {
            gc_dst->nlabels[i] = gc_src->nlabels[i] ;
            gc_dst->label_priors[i] =
              (float *)calloc(gc_src->nlabels[i],sizeof(float));
            if (!gc_dst->label_priors[i])
              ErrorExit(ERROR_NOMEMORY,
                        "GCAcreateWeightedFlashGCAfromParameterGCA: "
                        "to %d",gc_src->nlabels[i]);
            gc_dst->labels[i] =
              (unsigned short *)calloc(gc_src->nlabels[i], \
                                      sizeof(unsigned short)) ;
            if (!gc_dst->labels)
              ErrorExit(ERROR_NOMEMORY,
                        "GCAcreateWeightedFlashGCAfromParameterGCA:"
                        " to %d",gc_src->nlabels[i]);
            for (j = 0 ; j < gc_src->nlabels[i] ; j++)
            {
              gc_dst->label_priors[i][j]
              = gc_src->label_priors[i][j] ;
              gc_dst->labels[i][j]
              = gc_src->labels[i][j] ;
            }
          }

          /* now map intensity and covariance info over */
          T1 = gc_src->means[0] ;
          PD = gc_src->means[1] ;
          if (Ggca_label == gcan_dst->labels[n])
            label_count++ ;
          for (i = 0 ; i < gca_flash->ninputs ; i++)
          {
            gc_dst->means[i] =
              FLASHforwardModel(T1, PD, TR[i], fa[i], TE[i]) ;
            if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
                (Ggca_label < 0 ||
                 Ggca_label == gcan_src->labels[n]))
              printf("gcan(%d, %d, %d) %s: image[%d] "
                     "(fa=%2.1f) predicted mean "
                     "(%2.1f,%2.1f) --> %2.1f\n",
                     x, y, z,
                     cma_label_to_name(gcan_dst->labels[n]),
                     i, DEGREES(fa[i]),
                     T1, PD, gc_dst->means[i]) ;
            *MATRIX_RELT(m_jacobian, i+1, 1) =
              dFlash_dT1(T1, PD, TR[i], fa[i], TE[i]) ;
            *MATRIX_RELT(m_jacobian, i+1, 2) =
              dFlash_dPD(T1, PD, TR[i], fa[i], TE[i]) ;
            if (gcan_dst->labels[n] == Ggca_label)
            {
              label_means[i] += gc_dst->means[i] ;
            }
          }
#define MIN_T1 50
          if (T1 < MIN_T1)
          {
            gc_dst->regularized = 1 ;
            m_cov_dst = MatrixIdentity(gca_flash->ninputs,
                                       m_cov_dst) ;
          }
          else
          {
            m_cov_src =
              load_covariance_matrix(gc_src, m_cov_src,
                                     gca_T1PD->ninputs) ;
            m_jacobian_T = MatrixTranspose(m_jacobian,
                                           m_jacobian_T) ;
            m_tmp = MatrixMultiply(m_cov_src, m_jacobian_T, m_tmp) ;
            m_cov_dst = MatrixMultiply(m_jacobian,
                                       m_tmp, m_cov_dst) ;
          }
          for (v = i = 0 ; i < gca_flash->ninputs ; i++)
          {
            for (j = i ; j < gca_flash->ninputs ; j++, v++)
              gc_dst->covars[v] = *MATRIX_RELT(m_cov_dst, i+1, j+1) ;
          }
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label < 0 || Ggca_label == gcan_src->labels[n]))
          {
            printf("predicted covariance matrix:\n") ;
            MatrixPrint(stdout, m_cov_dst)  ;
          }
        }
      }
    }
  }

  if (Ggca_label >= 0)
  {
    printf("label %s (%d): means = ",
           cma_label_to_name(Ggca_label), Ggca_label) ;
    for (i = 0 ; i < gca_flash->ninputs ; i++)
    {
      label_means[i] /= (double)label_count ;
      printf("%2.1f ", label_means[i]) ;
    }
    printf("\n") ;
  }

  /* check and fix singular covariance matrixces */
  GCAregularizeCovarianceMatrices(gca_flash, lambda) ;
  GCAfixSingularCovarianceMatrices(gca_flash) ;
  MatrixFree(&m_jacobian) ;
  MatrixFree(&m_cov_src) ;
  MatrixFree(&m_cov_dst) ;
  MatrixFree(&m_jacobian_T) ;
  MatrixFree(&m_tmp) ;
  VectorFree(&v_wts) ;
  VectorFree(&v_wts_T) ;

  gca_flash->type = GCA_FLASH;
  GCAcopyDCToGCA(gca_T1PD, gca_flash) ;

  return(gca_flash) ;
}
GCA *
GCAcreateFlashGCAfromParameterGCA(GCA *gca_T1PD,
                                  double *TR, double *fa, double *TE,
                                  int nflash, double lambda)
{
  GCA        *gca_flash ;
  GCA_PRIOR  *gcap_src, *gcap_dst ;
  GCA_NODE   *gcan_src, *gcan_dst ;
  GC1D       *gc_src, *gc_dst ;
  MATRIX     *m_jacobian, *m_cov_src = NULL, *m_cov_dst = NULL, \
                                       *m_jacobian_T = NULL, *m_tmp = NULL ;
  int        n, x, y, z, i, j, v, label_count = 0 ;
  double     T1, PD, label_means[MAX_GCA_INPUTS] ;

  if (gca_T1PD->ninputs != 2)
    ErrorExit(ERROR_BADPARM,
              "GCAcreateFlashGCAfromParameterGCA: "
              "input gca must be T1/PD (ninputs=%d, should be 2",
              gca_T1PD->ninputs) ;
  // gca_flash will have gca->ninputs = nflash
  gca_flash = GCAalloc(nflash, gca_T1PD->prior_spacing, gca_T1PD->node_spacing,
                       gca_T1PD->node_width*gca_T1PD->node_spacing,
                       gca_T1PD->node_height*gca_T1PD->node_spacing,
                       gca_T1PD->node_depth*gca_T1PD->node_spacing,
                       GCA_NO_FLAGS) ;

  m_jacobian = MatrixAlloc(gca_flash->ninputs,
                           gca_T1PD->ninputs, MATRIX_REAL) ;

  /* first copy over priors */
  for (x = 0 ; x < gca_flash->prior_width ; x++)
  {
    for (y = 0 ; y < gca_flash->prior_height ; y++)
    {
      for (z = 0 ; z < gca_flash->prior_depth ; z++)
      {
        gcap_src = &gca_T1PD->priors[x][y][z] ;
        if (gcap_src == NULL)
          continue;
        gcap_dst = &gca_flash->priors[x][y][z] ;
        if (gcap_dst == NULL)
          continue;
        gcap_dst->nlabels = gcap_src->nlabels ;
        if (gcap_src->nlabels > gcap_dst->max_labels)
        {
          free(gcap_dst->priors) ;
          free(gcap_dst->labels) ;

          gcap_dst->labels =
            (unsigned short *)calloc(gcap_src->nlabels,
                                    sizeof(unsigned short)) ;
          if (!gcap_dst->labels)
            ErrorExit(ERROR_NOMEMORY,
                      "GCAcreateFlashGCAfromParameterGCA: "
                      "couldn't allocate %d labels",
                      gcap_src->nlabels) ;

          gcap_dst->priors =
            (float *)calloc(gcap_src->nlabels, sizeof(float)) ;
          if (!gcap_dst->priors)
            ErrorExit(ERROR_NOMEMORY,
                      "GCAcreateFlashGCAfromParameterGCA: "
                      "couldn't allocate %d priors",
                      gcap_src->nlabels) ;
          gcap_dst->max_labels = gcap_dst->nlabels ;
        }
        gcap_dst->total_training = gcap_src->total_training ;
        for (n = 0 ; n < gcap_src->nlabels ; n++)
        {
          gcap_dst->labels[n] = gcap_src->labels[n] ;
          gcap_dst->priors[n] = gcap_src->priors[n] ;
        }
      }
    }
  }

  /* now copy over classifiers and Markov stuff, */
  /* using Jacobian to map to new image space */
  for (x = 0 ; x < gca_flash->node_width ; x++)
  {
    for (y = 0 ; y < gca_flash->node_height ; y++)
    {
      for (z = 0 ; z < gca_flash->node_depth ; z++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak()  ;
        gcan_src = &gca_T1PD->nodes[x][y][z] ;
        gcan_dst = &gca_flash->nodes[x][y][z] ;
        gcan_dst->nlabels = gcan_src->nlabels ;
        gcan_dst->total_training = gcan_src->total_training ;
        if (gcan_src->nlabels > gcan_dst->max_labels)
        {
          free(gcan_dst->labels) ;
          free_gcs(gcan_dst->gcs,
                   gcan_dst->max_labels,
                   gca_flash->ninputs) ;

          gcan_dst->labels =
            (unsigned short *)calloc(gcan_src->nlabels,
                                    sizeof(unsigned short)) ;
          if (!gcan_dst->labels)
            ErrorExit(ERROR_NOMEMORY,
                      "GCAcreateFlashGCAfromParameterGCA: "
                      "couldn't allocate %d labels",
                      gcan_src->nlabels) ;

          gcan_dst->gcs = alloc_gcs(gcan_src->nlabels,
                                    GCA_NO_FLAGS, nflash) ;
          gcan_dst->max_labels = gcan_dst->nlabels ;
        }
        for (n = 0 ; n < gcan_src->nlabels ; n++)
        {
          gcan_dst->labels[n] = gcan_src->labels[n] ;
          gc_src = &gcan_src->gcs[n] ;
          gc_dst = &gcan_dst->gcs[n] ;
          gc_dst->ntraining = gc_src->ntraining ;
          gc_dst->n_just_priors = gc_src->n_just_priors ;
          for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
          {
            gc_dst->nlabels[i] = gc_src->nlabels[i] ;
            gc_dst->label_priors[i] =
              (float *)calloc(gc_src->nlabels[i],sizeof(float));
            if (!gc_dst->label_priors[i])
              ErrorExit(ERROR_NOMEMORY,
                        "GCAcreateFlashGCAfromParameterGCA: to %d",
                        gc_src->nlabels[i]);
            gc_dst->labels[i] =
              (unsigned short *)calloc(gc_src->nlabels[i], \
                                      sizeof(unsigned short)) ;
            if (!gc_dst->labels)
              ErrorExit(ERROR_NOMEMORY,
                        "GCAcreateFlashGCAfromParameterGCA: to %d",
                        gc_src->nlabels[i]);
            for (j = 0 ; j < gc_src->nlabels[i] ; j++)
            {
              gc_dst->label_priors[i][j]
              = gc_src->label_priors[i][j] ;
              gc_dst->labels[i][j]
              = gc_src->labels[i][j] ;
            }
          }

          /* now map intensity and covariance info over */
          T1 = gc_src->means[0] ;
          PD = gc_src->means[1] ;
          if (T1 < 0)
          {
            printf
            ("WARN: ******************************************\n");
            printf
            ("WARN: (%d, %d, %d) has T1 = %f < 0 and PD = %f\n",
             x, y, z, T1, PD);
            printf("WARN: nlabels = %d\n", gcan_src->nlabels);
            for (i=0;i < gcan_src->nlabels; ++i)
              printf
              ("WARN: %d: label = %d\n", i, gcan_src->labels[i]);
            printf("WARN: make T1 = 10 msec\n");
            printf
            ("WARN: ******************************************\n");
          }
          T1 = MAX(T1,10) ;
          PD = MAX(PD, 0) ;
          if (Ggca_label == gcan_dst->labels[n])
            label_count++ ;
          for (i = 0 ; i < gca_flash->ninputs ; i++)
          {
            gc_dst->means[i] =
              FLASHforwardModel(T1, PD, TR[i], fa[i], TE[i]) ;
            if (x == Gx && y == Gy && z == Gz &&
                (Ggca_label < 0
                 || Ggca_label == gcan_src->labels[n]))
              printf("gcan(%d, %d, %d) %s: image[%d] "
                     "(fa=%2.1f) predicted mean "
                     "(%2.1f,%2.1f) --> %2.1f\n",
                     x, y, z,
                     cma_label_to_name(gcan_dst->labels[n]),
                     i, DEGREES(fa[i]),
                     T1, PD, gc_dst->means[i]) ;
            *MATRIX_RELT(m_jacobian, i+1, 1) =
              dFlash_dT1(T1, PD, TR[i], fa[i], TE[i]) ;
            *MATRIX_RELT(m_jacobian, i+1, 2) =
              dFlash_dPD(T1, PD, TR[i], fa[i], TE[i]) ;
            if (gcan_dst->labels[n] == Ggca_label)
            {
              label_means[i] += gc_dst->means[i] ;
            }
          }
#define MIN_T1 50
          if (T1 < MIN_T1)
            m_cov_dst = MatrixIdentity(gca_flash->ninputs, m_cov_dst) ;
          else
          {
            m_cov_src =
              load_covariance_matrix(gc_src, m_cov_src,
                                     gca_T1PD->ninputs) ;
            m_jacobian_T = MatrixTranspose(m_jacobian,m_jacobian_T) ;
            m_tmp = MatrixMultiply(m_cov_src, m_jacobian_T, m_tmp) ;
            m_cov_dst = MatrixMultiply(m_jacobian,
                                       m_tmp,
                                       m_cov_dst) ;
          }
          for (v = i = 0 ; i < gca_flash->ninputs ; i++)
          {
            for (j = i ; j < gca_flash->ninputs ; j++, v++)
              gc_dst->covars[v] = *MATRIX_RELT(m_cov_dst, i+1, j+1) ;
          }
          if (x == Gx && y == Gy && z == Gz &&
              (Ggca_label < 0 || Ggca_label == gcan_src->labels[n]))
          {
            printf("predicted covariance matrix:\n") ;
            MatrixPrint(stdout, m_cov_dst)  ;
          }
        }
      }
    }
  }

  if (Ggca_label >= 0)
  {
    printf("label %s (%d): means = ",
           cma_label_to_name(Ggca_label), Ggca_label) ;
    for (i = 0 ; i < gca_flash->ninputs ; i++)
    {
      label_means[i] /= (double)label_count ;
      printf("%2.1f ", label_means[i]) ;
    }
    printf("\n") ;
  }

  /* check and fix singular covariance matrixces */
  GCAregularizeCovarianceMatrices(gca_flash, lambda) ;
  GCAfixSingularCovarianceMatrices(gca_flash) ;
  MatrixFree(&m_jacobian) ;
  MatrixFree(&m_cov_src) ;
  MatrixFree(&m_cov_dst) ;
  MatrixFree(&m_jacobian_T) ;
  MatrixFree(&m_tmp) ;

  gca_flash->type = GCA_FLASH;
  GCAcopyDCToGCA(gca_T1PD, gca_flash) ;

  return(gca_flash) ;
}

int
GCAfixSingularCovarianceMatrices(GCA *gca)
{
  int       x, y, z, fixed = 0, i, r, c, n, num, nparams, regularized = 0 ;
GCA_NODE  *gcan ;
GC1D      *gc ;
double    det, vars[MAX_GCA_INPUTS], min_det ;
  MATRIX    *m_cov_inv, *m_cov = NULL ;

  nparams = (gca->ninputs * (gca->ninputs+1))/2 + gca->ninputs ;
  /* covariance matrix and means */

  memset(vars, 0, sizeof(vars)) ;

	if (gca->total_training <= 1 && gca->prior_spacing <=1 && 
			gca->node_spacing <=1)  // degenerate case - can't estimate vars
	{
		for (num = 0, x = 0 ; x < gca->node_width ; x++)
		{
			for (y = 0 ; y < gca->node_height ; y++)
			{
				for (z = 0 ; z < gca->node_depth ; z++)
				{
					if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
						DiagBreak() ;
					gcan = &gca->nodes[x][y][z] ;
					for (n = 0 ; n < gcan->nlabels ; n++)
					{
						if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
								(Ggca_label == gcan->labels[n] || Ggca_label < 0))
							DiagBreak() ;
						gc = &gcan->gcs[n] ;
						for (r = 0 ; r < gca->ninputs ; r++)
							vars[r] += SQR((gc->means[r]*0.1)) ;
						num++ ;
					}
				}
			}
		}
	}
	else
	{
		for (num = 0, x = 0 ; x < gca->node_width ; x++)
		{
			for (y = 0 ; y < gca->node_height ; y++)
			{
				for (z = 0 ; z < gca->node_depth ; z++)
				{
					if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
						DiagBreak() ;
					gcan = &gca->nodes[x][y][z] ;
					for (n = 0 ; n < gcan->nlabels ; n++)
					{
						if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
								(Ggca_label == gcan->labels[n] || Ggca_label < 0))
							DiagBreak() ;
						gc = &gcan->gcs[n] ;
						det = covariance_determinant(gc, gca->ninputs) ;
						if ((gc->ntraining == 0 && det > 1) ||
								(gc->ntraining*gca->ninputs > 2.5*nparams))
							/* enough to estimate parameters */
						{
							m_cov = load_covariance_matrix(gc, m_cov, gca->ninputs) ;
							for (r = 0 ; r < gca->ninputs ; r++)
							{
								vars[r] += *MATRIX_RELT(m_cov,r+1, r+1) ;
							}
							num++ ;
						}
					}
				}
			}
		}
	}
  if (m_cov)
    MatrixFree(&m_cov) ;
  if (num >= 1)
  {
    printf("average std = ") ;
    for (min_det = 1.0, r = 0 ; r < gca->ninputs ; r++)
    {
      vars[r] /= (float)num ;
      printf("%2.1f ", sqrt(vars[r])) ;
      min_det *= vars[r] ;
    }
    min_det = min_det / pow(10.0, gca->ninputs) ;
    printf("  using min determinant for regularization = %2.1f\n", min_det) ;
  }
  else
    min_det = MIN_DET ;

  for (regularized = fixed = x = 0 ; x < gca->node_width ; x++)
    {
      for (y = 0 ; y < gca->node_height ; y++)
      {
        for (z = 0 ; z < gca->node_depth ; z++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;
          gcan = &gca->nodes[x][y][z] ;
          for (n = 0 ; n < gcan->nlabels ; n++)
          {
            if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
                (Ggca_label == gcan->labels[n] || Ggca_label < 0))
              DiagBreak() ;
            gc = &gcan->gcs[n] ;
            det = covariance_determinant(gc, gca->ninputs) ;
            m_cov_inv = load_inverse_covariance_matrix(gc,
                        NULL,
                        gca->ninputs) ;

            if (det <= 0 || m_cov_inv == NULL)
            {
              fixed ++ ;
							gc->regularized = 1 ;
							for (i = r = 0 ; r < gca->ninputs ; r++)
							{
								for (c = r ; c < gca->ninputs ; c++, i++)
								{
									if (r == c)
										gc->covars[i] += vars[r] ;
									/* mean of other variances at
										 this location */
								}
								if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
										(Ggca_label == gcan->labels[n] || Ggca_label < 0))
								{
									MATRIX *m ;
									printf("fixing singular covariance matrix for %s "
												 "@ (%d, %d, %d):\n",
												 cma_label_to_name(gcan->labels[n]),
												 x, y, z) ;
									m = load_covariance_matrix(gc, NULL, gca->ninputs) ;
									MatrixPrint(stdout, m) ;
									MatrixFree(&m) ;
								}
							}
						}
            else   /* not singular - check if it is ill-conditioned */
            {
              if (gc->regularized == 0)
                DiagBreak() ;
              if ((gc->ntraining*gca->ninputs
                   < 2*nparams && det < 0.1)
                  || (det < min_det))
              {
                gc->regularized = 1 ;
                regularized++ ;
                for (i = r = 0 ; r < gca->ninputs ; r++)
                {
                  for (c = r ; c < gca->ninputs ; c++, i++)
                  {
                    if (r == c)
                      gc->covars[i] += vars[r] ;
                    /* mean of overall variance
                       in this image */
                  }
                }
                if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
                    (Ggca_label == gcan->labels[n]
                     || Ggca_label < 0))
                {
                  MATRIX *m ;
                  printf("fixing ill-conditioned covariance "
                         "matrix for %s @ (%d, %d, %d):\n",
                         cma_label_to_name(gcan->labels[n]),
                         x, y, z) ;
                  m = load_covariance_matrix(gc,
                                             NULL, gca->ninputs) ;
                  MatrixPrint(stdout, m) ;
                  MatrixFree(&m) ;
                }
              }
            }

            if (m_cov_inv)
              MatrixFree(&m_cov_inv) ;
            det = covariance_determinant(gc, gca->ninputs) ;
            m_cov_inv = load_inverse_covariance_matrix(gc,
                        NULL,
                        gca->ninputs) ;
            if (det <= min_det || m_cov_inv == NULL)
            {
              printf("warning: regularization of node (%d, %d, %d) "
                     "label %s failed\n",
                     x, y, z, cma_label_to_name(gcan->labels[n])) ;
              DiagBreak() ;

            }
            if (m_cov_inv)
              MatrixFree(&m_cov_inv) ;
          }
        }
      }
    }

  printf("%d singular and %d ill-conditioned covariance"
         " matrices regularized\n",
         fixed , regularized) ;
  return(NO_ERROR) ;
}
int
GCAregularizeCovarianceMatrices(GCA *gca, double lambda)
{
  int       x, y, z, r, n, num, nparams ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  double    det, vars[MAX_GCA_INPUTS], min_det ;
  MATRIX    *m_cov = NULL ;

  nparams = (gca->ninputs * (gca->ninputs+1))/2 + gca->ninputs ;
  /* covariance matrix and means */

  memset(vars, 0, sizeof(vars)) ;

  for (num = 0, x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label == gcan->labels[n] || Ggca_label < 0))
            DiagBreak() ;
          gc = &gcan->gcs[n] ;
          det = covariance_determinant(gc, gca->ninputs) ;
          if ((gc->ntraining == 0 && det > 1) ||
              (gc->ntraining*gca->ninputs > 2.5*nparams))
            /* enough to estimate parameters */
          {
            m_cov = load_covariance_matrix(gc, m_cov, gca->ninputs) ;
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              vars[r] += *MATRIX_RELT(m_cov,r+1, r+1) ;
            }
            num++ ;
          }
        }
      }
    }
  }

  if (num >= 1)
  {
    printf("average std = ") ;
    for (min_det = 1.0, r = 0 ; r < gca->ninputs ; r++)
    {
      vars[r] /= (float)num ;
      printf("%2.1f ", sqrt(vars[r])) ;
      min_det *= vars[r] ;
    }
    min_det = min_det / pow(10.0, gca->ninputs) ;
    printf("  using min determinant for regularization = %2.1f\n", min_det) ;
  }
  else
    min_det = MIN_DET ;

  /* discard all previous stuff and just regularize by adding a */
  /* fixed constant independent of variance */
  for ( r = 0 ; r < gca->ninputs ; r++)
    vars[r] = lambda ;

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          int r, c, v ;
          MATRIX *m ;

          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label == gcan->labels[n] || Ggca_label < 0))
            DiagBreak() ;
          gc = &gcan->gcs[n] ;


          m = load_covariance_matrix(gc, NULL, gca->ninputs) ;
          for (r = v = 0 ; r < gca->ninputs ; r++)
            for (c = r ; c < gca->ninputs ; c++, v++)
            {
              if (r == c)
                gc->covars[v] += vars[r] ;
            }
          MatrixFree(&m) ;
          m = load_covariance_matrix(gc, NULL, gca->ninputs) ;
          v = 0 ;
        }
      }
    }
  }

  return(NO_ERROR) ;
}
int
GCAsetFlashParameters(GCA *gca, double *TRs, double *FAs, double *TEs)
{
  int n ;

  printf("setting GCA flash parameters to:\n") ;
  for (n = 0 ; n < gca->ninputs ; n++)
  {
    gca->TRs[n] = TRs[n] ;
    gca->FAs[n] = FAs[n] ;
    gca->TEs[n] = TEs[n] ;
    printf("TR=%2.1f msec, FA=%2.1f deg, TE=%2.1f msec\n",
           TRs[n], DEGREES(FAs[n]), TEs[n]) ;
  }
  gca->type=GCA_FLASH; // mark gca type
  return(NO_ERROR) ;
}

GCA *
GCAcreateFlashGCAfromFlashGCA(GCA *gca_flash_src,
                              double *TR, double *fa,
                              double *TE, int nflash)
{
  GCA        *gca_flash_dst ;
  GCA_PRIOR  *gcap_src, *gcap_dst ;
  GCA_NODE   *gcan_src, *gcan_dst ;
  GC1D       *gc_src, *gc_dst ;
  MATRIX     *m_jac_dst, *m_jac_src, *m_cov_src,
  *m_cov_dst, *m_pinv_src, *m_cov_T1PD ;
  int        n, x, y, z, i, j, v, label_count = 0 ;
  double     T1, PD, label_means[MAX_GCA_INPUTS] ;

  m_cov_T1PD = m_cov_dst = m_pinv_src = m_cov_src = NULL ;
  FlashBuildLookupTables(gca_flash_src->ninputs,
                         gca_flash_src->TRs,
                         gca_flash_src->FAs,
                         gca_flash_src->TEs) ;

  gca_flash_dst =
    GCAalloc(nflash, gca_flash_src->prior_spacing, gca_flash_src->node_spacing,
             gca_flash_src->node_width*gca_flash_src->node_spacing,
             gca_flash_src->node_height*gca_flash_src->node_spacing,
             gca_flash_src->node_depth*gca_flash_src->node_spacing,
             GCA_NO_FLAGS) ;

  for (n = 0 ; n < nflash ; n++)
  {
    gca_flash_dst->TRs[n] = TR[n] ;
    gca_flash_dst->FAs[n] = fa[n] ;
    gca_flash_dst->TEs[n] = TE[n] ;
  }

  m_jac_src = MatrixAlloc(gca_flash_src->ninputs, 2, MATRIX_REAL) ;
  /* T1/PD --> src jacobian */
  m_jac_dst = MatrixAlloc(gca_flash_dst->ninputs, 2, MATRIX_REAL) ;
  /* T1/PD --> dst jacobian */

  /* first copy over priors */
  for (x = 0 ; x < gca_flash_dst->prior_width ; x++)
  {
    for (y = 0 ; y < gca_flash_dst->prior_height ; y++)
    {
      for (z = 0 ; z < gca_flash_dst->prior_depth ; z++)
      {
        gcap_src = &gca_flash_src->priors[x][y][z] ;
        if (gcap_src == NULL)
          continue;
        gcap_dst = &gca_flash_dst->priors[x][y][z] ;
        if (gcap_dst == NULL)
          continue;
        gcap_dst->nlabels = gcap_src->nlabels ;
        if (gcap_src->nlabels > gcap_dst->max_labels)
        {
          free(gcap_dst->priors) ;
          free(gcap_dst->labels) ;

          gcap_dst->labels =
            (unsigned short *)calloc(gcap_src->nlabels,
                                    sizeof(unsigned short)) ;
          if (!gcap_dst->labels)
            ErrorExit(ERROR_NOMEMORY,
                      "GCAcreateFlashGCAfromFlashGCA: "
                      "couldn't allocate %d labels",
                      gcap_src->nlabels) ;

          gcap_dst->priors =
            (float *)calloc(gcap_src->nlabels, sizeof(float)) ;
          if (!gcap_dst->priors)
            ErrorExit(ERROR_NOMEMORY,
                      "GCAcreateFlashGCAfromFlashGCA: "
                      "couldn't allocate %d priors",
                      gcap_src->nlabels) ;
          gcap_dst->max_labels = gcap_dst->nlabels ;
        }
        gcap_dst->total_training = gcap_src->total_training ;
        for (n = 0 ; n < gcap_src->nlabels ; n++)
        {
          gcap_dst->labels[n] = gcap_src->labels[n] ;
          gcap_dst->priors[n] = gcap_src->priors[n] ;
        }
      }
    }
  }

  /* now copy over classifiers and Markov stuff, */
  /* using Jacobian to map to new image space */
  for (x = 0 ; x < gca_flash_dst->node_width ; x++)
  {
    for (y = 0 ; y < gca_flash_dst->node_height ; y++)
    {
      for (z = 0 ; z < gca_flash_dst->node_depth ; z++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak()  ;
        gcan_src = &gca_flash_src->nodes[x][y][z] ;
        gcan_dst = &gca_flash_dst->nodes[x][y][z] ;
        gcan_dst->nlabels = gcan_src->nlabels ;
        gcan_dst->total_training = gcan_src->total_training ;
        if (gcan_src->nlabels > gcan_dst->max_labels)
        {
          free(gcan_dst->labels) ;
          for (n = 0 ; n < gcan_dst->max_labels ; n++)
          {
            gc_dst = &gcan_dst->gcs[n] ;
            for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
            {
              if (gc_dst->label_priors[i])
                free(gc_dst->label_priors[i]) ;
              if (gc_dst->labels[i])
                free(gc_dst->labels[i]) ;
            }
            if (gc_dst->nlabels)
              free(gc_dst->nlabels) ;
            if (gc_dst->labels)
              free(gc_dst->labels) ;
            if (gc_dst->label_priors)
              free(gc_dst->label_priors) ;
          }

          gcan_dst->labels =
            (unsigned short *)calloc(gcan_src->nlabels,
                                    sizeof(unsigned short)) ;
          if (!gcan_dst->labels)
            ErrorExit(ERROR_NOMEMORY, "GCAcreateFlashGCAfromFlashGCA: "
                      "couldn't allocate %d labels",
                      gcan_src->nlabels) ;

          gcan_dst->gcs = alloc_gcs(gcan_src->nlabels,
                                    GCA_NO_FLAGS, nflash) ;
        }
        for (n = 0 ; n < gcan_src->nlabels ; n++)
        {
          gcan_dst->labels[n] = gcan_src->labels[n] ;
          gc_src = &gcan_src->gcs[n] ;
          gc_dst = &gcan_dst->gcs[n] ;
          gc_dst->ntraining = gc_src->ntraining ;
          gc_dst->n_just_priors = gc_src->n_just_priors ;
          for (i = 0 ; i < GIBBS_NEIGHBORS ; i++)
          {
            gc_dst->nlabels[i] = gc_src->nlabels[i] ;
            gc_dst->label_priors[i] =
              (float *)calloc(gc_src->nlabels[i],sizeof(float));
            if (!gc_dst->label_priors[i])
              ErrorExit(ERROR_NOMEMORY,
                        "GCAcreateFlashGCAfromFlashGCA: to %d",
                        gc_src->nlabels[i]);
            gc_dst->labels[i] =
              (unsigned short *)calloc(gc_src->nlabels[i],
                                      sizeof(unsigned short)) ;
            if (!gc_dst->labels)
              ErrorExit(ERROR_NOMEMORY,
                        "GCAcreateFlashGCAfromFlashGCA: to %d",
                        gc_src->nlabels[i]);
            for (j = 0 ; j < gc_src->nlabels[i] ; j++)
            {
              gc_dst->label_priors[i][j]
              = gc_src->label_priors[i][j] ;
              gc_dst->labels[i][j]
              = gc_src->labels[i][j] ;
            }
          }

          /* now map intensity and covariance info over */
          compute_T1_PD(gca_flash_src->ninputs,
                        gc_src->means,
                        gca_flash_src->TRs,
                        gca_flash_src->FAs,
                        gca_flash_src->TEs,
                        &T1, &PD) ;
          T1 = MAX(T1,10) ;
          PD = MAX(PD,0) ;
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label < 0 || Ggca_label == gcan_src->labels[n]))
          {
            int j ;
            printf("gcan(%d, %d, %d) %s: means=[",
                   x, y, z, cma_label_to_name(gcan_src->labels[n])) ;
            for (j = 0 ; j < gca_flash_src->ninputs ; j++)
              printf(" %2.1f", gc_src->means[j]) ;
            printf("] T1/PD=%2.1f/%2.1f\n", T1, PD) ;
          }
          if (Ggca_label == gcan_dst->labels[n])
            label_count++ ;
          for (i = 0 ; i < gca_flash_dst->ninputs ; i++)
          {
            gc_dst->means[i] =
              FLASHforwardModel(T1, PD, TR[i], fa[i], TE[i]) ;
            if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
                (Ggca_label < 0
                 || Ggca_label == gcan_src->labels[n]))
              printf("gcan(%d, %d, %d) %s: image[%d] "
                     "(fa=%2.1f) predicted mean "
                     "(%2.1f,%2.1f) --> %2.1f\n",
                     x, y, z,
                     cma_label_to_name(gcan_dst->labels[n]),
                     i, DEGREES(fa[i]),
                     T1, PD, gc_dst->means[i]) ;
            *MATRIX_RELT(m_jac_dst, i+1, 1) =
              dFlash_dT1(T1, PD, gca_flash_dst->TRs[i],
                         gca_flash_dst->FAs[i],
                         gca_flash_dst->TEs[i]) ;
            *MATRIX_RELT(m_jac_dst, i+1, 2) =
              dFlash_dPD(T1, PD, gca_flash_dst->TRs[i],
                         gca_flash_dst->FAs[i],
                         gca_flash_dst->TEs[i]) ;

            //  *MATRIX_RELT(m_jac_src, i+1, 1) =
            //  dFlash_dT1(T1, PD, gca_flash_src->TRs[i],
            // gca_flash_src->FAs[i], gca_flash_src->TEs[i]) ;
            //  *MATRIX_RELT(m_jac_src, i+1, 2) =
            //  dFlash_dPD(T1, PD, gca_flash_src->TRs[i],
            //gca_flash_src->FAs[i], gca_flash_src->TEs[i]) ;
            if (gcan_dst->labels[n] == Ggca_label)
            {
              label_means[i] += gc_dst->means[i] ;
            }
          }
          //this allows src and dst have different ninputs
          for (i = 0 ; i < gca_flash_src->ninputs ; i++)
          {
            *MATRIX_RELT(m_jac_src, i+1, 1) =
              dFlash_dT1(T1, PD, gca_flash_src->TRs[i],
                         gca_flash_src->FAs[i],
                         gca_flash_src->TEs[i]) ;
            *MATRIX_RELT(m_jac_src, i+1, 2) =
              dFlash_dPD(T1, PD, gca_flash_src->TRs[i],
                         gca_flash_src->FAs[i],
                         gca_flash_src->TEs[i]) ;
          }

#define MIN_T1 50
          if (x == Gx && y == Gy && z == Gz)
            DiagBreak()  ;
          if (T1 < MIN_T1)
            m_cov_dst = MatrixIdentity(gca_flash_dst->ninputs,
                                       m_cov_dst) ;
          else
          {
            m_cov_src =
              load_covariance_matrix(gc_src, m_cov_src,
                                     gca_flash_src->ninputs) ;
            m_pinv_src = MatrixPseudoInverse(m_jac_src, m_pinv_src) ;
            m_cov_T1PD =
              MatrixSimilarityTransform(m_cov_src,
                                        m_pinv_src,
                                        m_cov_T1PD) ;
            m_cov_dst = MatrixSimilarityTransform(m_cov_T1PD,
                                                  m_jac_dst,
                                                  m_cov_dst) ;
          }
          for (v = i = 0 ; i < gca_flash_dst->ninputs ; i++)
          {
            for (j = i ; j < gca_flash_dst->ninputs ; j++, v++)
              gc_dst->covars[v] = *MATRIX_RELT(m_cov_dst, i+1, j+1) ;
          }
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label < 0 || Ggca_label == gcan_src->labels[n]))
          {
            printf("predicted covariance matrix:\n") ;
            MatrixPrint(stdout, m_cov_dst)  ;
          }
        }
      }
    }
  }

  if (Ggca_label >= 0)
  {
    printf("label %s (%d): means = ",
           cma_label_to_name(Ggca_label), Ggca_label) ;
    for (i = 0 ; i < gca_flash_dst->ninputs ; i++)
    {
      label_means[i] /= (double)label_count ;
      printf("%2.1f ", label_means[i]) ;
    }
    printf("\n") ;
  }

  /* check and fix singular covariance matrixces */
  GCAfixSingularCovarianceMatrices(gca_flash_dst) ;
  MatrixFree(&m_jac_dst) ;
  MatrixFree(&m_jac_src) ;
  if (m_cov_src)
    MatrixFree(&m_cov_src) ;
  MatrixFree(&m_cov_dst) ;
  if (m_pinv_src)
    MatrixFree(&m_pinv_src) ;
  if (m_cov_T1PD)
    MatrixFree(&m_cov_T1PD) ;

  gca_flash_dst->type = GCA_FLASH;
  GCAcopyDCToGCA(gca_flash_src, gca_flash_dst) ;
  return(gca_flash_dst) ;
}

int
GCAnormalizeMeans(GCA *gca, float target)
{
  int       x, y, z, frame, n ;
  double    norm ;
  Real      val ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          gc = &gcan->gcs[n] ;
          for (frame = 0, norm = 0 ; frame < gca->ninputs ; frame++)
          {
            val = gc->means[frame] ;
            norm += (val*val) ;
          }
          norm = sqrt(norm) / target ;
          if (FZERO(norm))
            norm = 1 ;
          for (frame = 0 ; frame < gca->ninputs ; frame++)
            gc->means[frame] /= norm ;
        }
      }
    }
  }

  return(NO_ERROR) ;
}
int
GCAregularizeCovariance(GCA *gca, float regularize)
{
  int       x, y, z, i, r, c, n, num, nparams ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  double    det, vars[MAX_GCA_INPUTS] ;
  MATRIX    *m_cov = NULL ;

  nparams = (gca->ninputs * (gca->ninputs+1))/2 + gca->ninputs ;
  /* covariance matrix and means */
  memset(vars, 0, sizeof(vars)) ;
  for (num = 0, x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label == gcan->labels[n] || Ggca_label < 0))
            DiagBreak() ;
          gc = &gcan->gcs[n] ;
          det = covariance_determinant(gc, gca->ninputs) ;
          if ((gc->ntraining == 0 && det > 1) ||
              (gc->ntraining*gca->ninputs > 2.5*nparams))
            /* enough to estimate parameters */
          {
            m_cov = load_covariance_matrix(gc, m_cov, gca->ninputs) ;
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              vars[r] += *MATRIX_RELT(m_cov,r+1, r+1) ;
            }
            num++ ;
          }
        }
      }
    }
  }
  if (m_cov)
    MatrixFree(&m_cov) ;
  if (num >= 1)
  {
    for (r = 0 ; r < gca->ninputs ; r++)
    {
      vars[r] /= (float)num ;
      printf("average std[%d] = %2.1f\n", r, sqrt(vars[r])) ;
    }
  }
  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label == gcan->labels[n] || Ggca_label < 0))
            DiagBreak() ;
          gc = &gcan->gcs[n] ;
          for (i = r = 0 ; r < gca->ninputs ; r++)
          {
            for (c = r ; c < gca->ninputs ; c++, i++)
            {
              gc->covars[i] = (1-regularize)*gc->covars[i] ;
              if (r == c)
                gc->covars[i] += regularize*vars[r] ;
            }
          }
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z &&
              (Ggca_label == gcan->labels[n] || Ggca_label < 0))
          {
            MATRIX *m ;
            printf("regularizing covariance matrix "
                   "for %s @ (%d, %d, %d):\n",
                   cma_label_to_name(gcan->labels[n]), x, y, z) ;
            m = load_covariance_matrix(gc, NULL, gca->ninputs) ;
            MatrixPrint(stdout, m) ;
            MatrixFree(&m) ;
          }
        }
      }
    }
  }
  return(NO_ERROR) ;
}

MATRIX  *
GCAlabelCovariance(GCA *gca, int label, MATRIX *m_total)
{
  int       xn, yn, zn, n ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  double    wt ;
  float     prior ;
  MATRIX    *m_cov = NULL ;

  /* compute overall white matter mean to use as anchor for rescaling */
  for (wt = 0.0, zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          /* find index in lookup table for this label */
          if (gcan->labels[n] != label)
            continue ;
          gc = &gcan->gcs[n] ;
          prior = get_node_prior(gca, label, xn, yn, zn);
          if (prior != 0)
          {
            wt += prior ;
            m_cov = load_covariance_matrix(gc, m_cov, gca->ninputs) ;
            MatrixScalarMul(m_cov, prior, m_cov) ;
            if (m_total == NULL)
              m_total = MatrixCopy(m_cov, NULL) ;
            else
              MatrixAdd(m_cov, m_total, m_total) ;
          }
        }
      }
    }
  }

  if (m_total == NULL)
    return(NULL) ;

  if (FZERO(wt))
    wt = 1 ;
  MatrixScalarMul(m_total, 1/wt, m_total) ;
  MatrixFree(&m_cov) ;
  return(m_total) ;
}

int
GCAlabelMeanFromImage(GCA *gca, TRANSFORM *transform,
                      MRI *mri, int label, float *means)
{
  int       xn, yn, zn, n, r, xv, yv, zv ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  double    wt, val ;
  float     prior ;
  double MIN_MEAN_PRIOR = 0.5 ;

  /* compute overall white matter mean to use as anchor for rescaling */
  memset(means, 0, gca->ninputs*sizeof(float)) ;
  for (wt = 0.0, zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        if (GCAnodeToSourceVoxel(gca, mri, transform,
                                 xn, yn, zn, &xv, &yv, &zv)==NO_ERROR)
        {
          for (n = 0 ; n < gcan->nlabels ; n++)
          {
            /* find index in lookup table for this label */
            if (gcan->labels[n] != label)
              continue ;
            gc = &gcan->gcs[n] ;
            prior = get_node_prior(gca, label, xn, yn, zn) ;
            if (prior < MIN_MEAN_PRIOR)
              continue ;
            wt += prior ;
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              MRIsampleVolumeFrame(mri, xv, yv, zv, r, &val) ;
              means[r] += val*prior ;
              if (!finite(gc->means[r]))
                DiagBreak() ;
            }
          }
        }
      }
    }
  }
  for (r = 0 ; r < gca->ninputs ; r++)
    means[r] /= wt ;
  return(NO_ERROR) ;
}


/* don't try to estimate cortex directly - too hard to
   get alignment. We'll estimate it from other gm classes
   but how about just use initial linear registration?
*/
#if 1
static int align_labels[] =
  {
    Right_Pallidum,
    Left_Pallidum,
    Right_Lateral_Ventricle,
    Left_Lateral_Ventricle,
    Right_Hippocampus,
    Left_Hippocampus,
    Right_Cerebral_White_Matter,
    Left_Cerebral_White_Matter,
    Left_Cerebral_Cortex,
    Right_Cerebral_Cortex,
#if 0
    Left_Inf_Lat_Vent,
    Right_Inf_Lat_Vent,
#endif
    Right_Caudate,
    Left_Caudate,
    Left_Cerebellum_Cortex,
    Right_Cerebellum_Cortex,
    Left_Cerebellum_White_Matter,
    Right_Cerebellum_White_Matter,
    Left_Amygdala,
    Right_Amygdala,
    Left_Thalamus_Proper,
    Right_Thalamus_Proper,
    Left_Putamen,
    Right_Putamen,
    Brain_Stem,
    Right_VentralDC,
    Left_VentralDC,
    Third_Ventricle,
    Fourth_Ventricle
  } ;
#else
static int align_labels[] =
  {
    Right_Cerebellum_White_Matter,
  } ;
#endif


#define NALIGN_LABELS (sizeof(align_labels) / sizeof(align_labels[0]))
#define BORDER_SIZE 2
#define WM_BORDER_SIZE 5

static int gm_labels[] =
  {
    Left_Hippocampus,
    Right_Hippocampus,
    Left_Amygdala,
    Right_Amygdala,
    //    Left_Caudate,
    //    Right_Caudate,
    Left_Cerebral_Cortex,
    Right_Cerebral_Cortex
    //          Left_Putamen,
    //  Right_Putamen
  } ;

#define NGM_LABELS (sizeof(gm_labels) / sizeof(gm_labels[0]))

static int wm_labels[] =
  {
    Left_Cerebral_White_Matter,
    Right_Cerebral_White_Matter
  } ;
#define NWM_LABELS (sizeof(wm_labels) / sizeof(wm_labels[0]))


static int csf_labels[] =
  {
    Left_Lateral_Ventricle,
    Right_Lateral_Ventricle,
    Third_Ventricle,
    Fourth_Ventricle
  } ;

#define NCSF_LABELS (sizeof(csf_labels) / sizeof(csf_labels[0]))

#if 0 //This is Bruce's version
int
GCAmapRenormalizeWithAlignment(GCA *gca,
                               MRI *mri,
                               TRANSFORM *transform,
                               FILE *logfp,
                               char *base_name,
                               LTA **plta)
{
  HISTOGRAM *h_mri, *h_gca ;
  int       l, nbins, i, x, y, z, xn, yn, zn, num, frame, \
  bin, j, n, computed[MAX_CMA_LABELS], b, label, k,
  border = BORDER_SIZE, peak ;
  float     fmin, fmax, label_scales[MAX_CMA_LABELS], scale_factor, overlap,
  mean_gm_scale, mean_wm_scale, mean_csf_scale,
  label_offsets[MAX_CMA_LABELS], mean_wm_offset, \
  mean_csf_offset, mean_gm_offset ;
  Real      val/*, scale*/ ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  MRI       *mri_seg = NULL, *mri_aligned, *mri_labels = NULL ;
  char      fname[STRLEN] ;
  MATRIX    *m_L, *m_by_label[MAX_CMA_LABELS] ;
  LTA       *lta ;

  printf("renormalizing by structure alignment....\n") ;
  if (plta)
    lta = *plta ;
  else
    lta = NULL ;
  for (frame = 0 ; frame < mri->nframes ; frame++)
  {
    for (l = 0 ; l < MAX_CMA_LABELS ; l++)
    {
      if (l == Gdiag_no)
        DiagBreak() ;
      label_scales[l] = 1.0 ;
      label_offsets[l] = 0.0 ;
      computed[l] = 0 ;
      m_by_label[l] = NULL ;  // not estimated yet
    }

    printf("renormalizing input #%d\n", frame) ;
    MRIvalRangeFrame(mri, &fmin, &fmax, frame) ;
    nbins = 256 ;
    h_mri = HISTOalloc(nbins) ;
    for (j = 0 ; j < NALIGN_LABELS ; j++)
    {
      l = align_labels[j] ;
      if (l == Gdiag_no)
        DiagBreak() ;

      mri_seg = MRIclone(mri, mri_seg) ;
      mri_labels = MRIclone(mri, mri_labels) ;

      /* include 2 voxel border to get context around structure.
         e.g. hippo is made easier to find by wm inferior and ventricle
         posterior.
      */
      if (IS_HIPPO(l) || IS_AMYGDALA(l))
        border = BORDER_SIZE+1 ;  // need more context for hippo
      else
        border = BORDER_SIZE ;
      GCAbuildMostLikelyVolumeForStructure
      (gca, mri_seg, l, border, transform,mri_labels) ;
      for (x = 0 ; x < mri_labels->width ; x++)
      {
        for (y = 0 ; y < mri_labels->height ; y++)
        {
          for (z = 0 ; z < mri_labels->depth ; z++)
          {
            if (x == Gx && y == Gy && z == Gz)
              DiagBreak() ;
            label = nint(MRIgetVoxVal(mri_labels, x, y, z, 0)) ;
            if (computed[label] == 0)
              continue ;
            val = MRIgetVoxVal(mri_seg, x, y, z, frame) ;
            val = val * label_scales[label] + label_offsets[label] ;
            MRIsetVoxVal(mri_seg, x, y, z, frame, val) ;
          }
        }
      }

      /* ventricle at the posterior part of hippo frequently
         makes local minima
         in alignment energy functional - remove them.
      */
#if 1
      if (l == Left_Hippocampus || l == Right_Hippocampus)
      {
        for (x = 0 ; x < mri_labels->width ; x++)
        {
          for (y = 0 ; y < mri_labels->height ; y++)
          {
            for (z = 0 ; z < mri_labels->depth ; z++)
            {
              if (x == Gx && y == Gy && z == Gz)
                DiagBreak() ;
              label = MRIgetVoxVal(mri_labels, x, y, z, 0) ;
              if (IS_LAT_VENT(label) || IS_INF_LAT_VENT(label))
                MRIsetVoxVal(mri_seg, x, y, z, frame, 0) ;
            }
          }
        }
      }
#endif
      if (l == Left_Cerebral_White_Matter ||
          l == Right_Cerebral_White_Matter)
      {
        MRI *mri_tmp, *mri_border ;

        // create a volume that is the wm eroded
        // 3 times, plus the border voxels
        mri_tmp = MRIclone(mri_seg, NULL) ;
        GCAbuildMostLikelyVolumeForStructure
        (gca, mri_tmp, l, 0, transform, NULL) ;
        mri_border = MRIsubtract(mri_seg, mri_tmp, NULL) ;
        // just outside

        // erode just the interior 3 times to get to high prob regions
        MRIerode(mri_tmp, mri_tmp) ;
        MRIerode(mri_tmp, mri_tmp) ;
        MRIerode(mri_tmp, mri_tmp) ;
        MRIadd(mri_tmp, mri_border, mri_seg) ;  // border + interior
        MRIfree(&mri_tmp) ;
        MRIfree(&mri_border) ;
      }

      if (Gdiag & DIAG_WRITE)
      {
        sprintf(fname, "%s_label%d.mgz", base_name, l) ;
        MRIwrite(mri_seg, fname) ;
      }
      if (transform->type != MORPH_3D_TYPE)
      {
        if (lta)   // try to find a previously computed one
        {
          for (n = 0 ; n < lta->num_xforms ; n++)
            if (lta->xforms[n].label == l)
              break ;
          if (n >= lta->num_xforms)
            n = -1 ;  // indicate no xform found
        }
        else   // no transform specified by caller
          n = -1 ;
        if (n < 0)  // no transform - compute one
        {
          double det ;
          float  evalues[4] ;
          MATRIX *m_evectors ;

          printf("aligning %s...\n", cma_label_to_name(l)) ;
          fflush(stdout);
          m_L = MRIgetVoxelToVoxelXform(mri_seg, mri) ;
          MRIpowellAlignImages
          (mri_seg, mri,  m_L, &scale_factor, NULL) ;
          det = MatrixDeterminant(m_L) ;
          m_evectors = MatrixEigenSystem(m_L, evalues, NULL) ;
          printf("eigen values (%2.2f, %2.2f, %2.2f, %2.2f), "
                 "vectors:\n",
                 evalues[0], evalues[1], evalues[2], evalues[3]) ;
          MatrixPrint(stdout, m_evectors) ;
          MatrixFree(&m_evectors) ;
          if (det < 0.1 || det > 4 ||
              scale_factor > 3 || scale_factor<0.3 || evalues[3] < 0.2)
          {
            printf("invalid transform detected "
                   "(det=%2.4f, iscale=%2.4f\n",
                   det, scale_factor) ;
            MatrixFree(&m_L) ;
            m_L = MRIgetVoxelToVoxelXform(mri_seg, mri) ;

          }
        }
        else   // use previously computed transform
          m_L = MatrixCopy(lta->xforms[n].m_L, NULL) ;

        if (l == Gdiag_no)
          DiagBreak() ;

        if (Gdiag & DIAG_WRITE)
        {
          sprintf(fname, "%s_label%d_after.mgz", base_name, l) ;
          mri_aligned = MRIlinearTransform(mri_seg, NULL, m_L) ;
          MRIwrite(mri_aligned, fname) ;
          MRIfree(&mri_aligned) ;
        }

        if (l == Left_Cerebral_White_Matter ||
            l == Right_Cerebral_White_Matter)
        {
          // wm so big it's hard to localize with a linear xform
          GCAbuildMostLikelyVolumeForStructure
          (gca, mri_seg, l, 0, transform, NULL) ;
          MRIerode(mri_seg, mri_seg) ;
          MRIerode(mri_seg, mri_seg) ;
        }
        else
        {
          /* put ventricles back in for erosion to remove
             (otherwise a bunch of hippo
             gets removed */
          if (l == Left_Hippocampus || l == Right_Hippocampus)
          {
            for (x = 0 ; x < mri_labels->width ; x++)
            {
              for (y = 0 ; y < mri_labels->height ; y++)
              {
                for (z = 0 ; z < mri_labels->depth ; z++)
                {
                  if (x == Gx && y == Gy && z == Gz)
                    DiagBreak() ;
                  label =
                    MRIgetVoxVal(mri_labels, x, y, z, 0) ;
                  if (IS_LAT_VENT(label) ||
                      IS_INF_LAT_VENT(label))
                    MRIsetVoxVal
                    (mri_seg, x, y, z, frame, 128) ;
                }
              }
            }
          }
          for (b = 0 ; b < border ; b++)
            MRIerode(mri_seg, mri_seg) ; // get rid of outside border
          MRIerode(mri_seg, mri_seg) ; // get rid of inside border
        }

        mri_aligned = MRIlinearTransform(mri_seg, NULL, m_L) ;
      }
      else  // 3d morph already done - don't bother aligning
      {
        m_L = NULL ;
        if (l == Left_Cerebral_White_Matter ||
            l == Right_Cerebral_White_Matter)
        {
          // wm so big it's hard to localize with a linear xform
          GCAbuildMostLikelyVolumeForStructure
          (gca, mri_seg, l, 0, transform, NULL) ;
          MRIerode(mri_seg, mri_seg) ;
        }
        else
        {
          /* put ventricles back in for erosion
             to remove (otherwise a bunch of hippo
             gets removed */
          if (l == Left_Hippocampus || l == Right_Hippocampus)
          {
            for (x = 0 ; x < mri_labels->width ; x++)
            {
              for (y = 0 ; y < mri_labels->height ; y++)
              {
                for (z = 0 ; z < mri_labels->depth ; z++)
                {
                  if (x == Gx && y == Gy && z == Gz)
                    DiagBreak() ;
                  label =
                    MRIgetVoxVal(mri_labels, x, y, z, 0) ;
                  if (IS_LAT_VENT(label) ||
                      IS_INF_LAT_VENT(label))
                    MRIsetVoxVal
                    (mri_seg, x, y, z, frame, 128) ;
                }
              }
            }
          }
          for (b = 0 ; b < border ; b++)
            MRIerode(mri_seg, mri_seg) ; // get rid of outside border
        }
        mri_aligned = MRIerode(mri_seg, NULL) ;
        // get rid of inside border
      }

      MRIbinarize(mri_aligned, mri_aligned, 1, 0, 128) ;
      if (Gdiag & DIAG_WRITE)
      {
        sprintf(fname, "%s_label%d_eroded.mgz", base_name, l) ;
        MRIwrite(mri_aligned, fname) ;
      }
      if (l == Gdiag_no)
        DiagBreak() ;
      HISTOclear(h_mri, h_mri) ;
      h_mri->bin_size = (fmax-fmin)/255.0 ;
      if (h_mri->bin_size < 1 &&
          (mri->type == MRI_UCHAR || mri->type == MRI_SHORT))
        h_mri->bin_size = 1 ;
      for (i = 0 ; i < nbins ; i++)
        h_mri->bins[i] = (i+1)*h_mri->bin_size ;

      for (num = x = 0 ; x < mri_aligned->width ; x++)
      {
        for (y = 0 ; y < mri_aligned->height ; y++)
        {
          for (z = 0 ; z < mri_aligned->depth ; z++)
          {
            if (x == Gx && y == Gy && z == Gz)
              DiagBreak() ;
            MRIsampleVolumeFrame(mri_aligned, x, y, z, frame, &val) ;
            if (DZERO(val))  // not in this structure
              continue ;
            MRIsampleVolumeFrame(mri, x, y, z, frame, &val) ;

            if (FZERO(val))  // skull stripped
              continue ;
            bin = nint((val - fmin)/h_mri->bin_size) ;
            if (bin >= h_mri->nbins)
              bin = h_mri->nbins-1 ;
            else if (bin < 0)
              bin = 0 ;

            h_mri->counts[bin]++ ;
            num++ ;
          }
        }
      }
      MRIfree(&mri_aligned) ;
      peak = HISTOfindHighestPeakInRegion(h_mri, 0, h_mri->nbins) ;
      HISTOfillHoles(h_mri) ;
      HISTOmakePDF(h_mri, h_mri) ;
      printf("peak = %2.5f (%d)\n", h_mri->counts[peak], peak) ;
      if ((num <= 50) ||
          ((transform->type != MORPH_3D_TYPE) &&
           (h_mri->counts[peak] < 0.05)))
      {
        if (h_mri->counts[peak] < .05)
          printf("uniform distribution in %s MR - "
                 "rejecting arbitrary fit\n",
                 cma_label_to_name(l)) ;
        if (m_L)
          MatrixFree(&m_L) ;
        continue ;
      }
      if (m_L)
      {
        if (plta)
          m_by_label[l] = m_L ;  // store if for assembling an LTA later
        else
          MatrixFree(&m_L) ;
      }
      h_gca = gcaGetLabelHistogram(gca, l, 0) ;
      HISTOmakePDF(h_gca, h_gca) ;

      {
        sprintf(fname, "%s_label%d_mri.plt", base_name, l) ;
        HISTOplot(h_mri, fname) ;
        sprintf(fname, "%s_label%d_gca.plt", base_name, l) ;
        HISTOplot(h_gca, fname) ;
        DiagBreak() ;
      }
      overlap = HISTOthreshSum(h_mri, h_gca, .025) ;
      if (overlap > 0.01)
      {
        //                        if (l == Gdiag_no)
        HISTOfindLinearFit(h_gca, h_mri, .025, 10, -75, 75,
                           &label_scales[l],
                           &label_offsets[l]) ;
        computed[l] = 1 ;
        printf("%s (%d): linear fit = %2.2f x + %2.1f "
               "(%d voxels, overlap=%2.3f)\n",
               cma_label_to_name(l), l,
               label_scales[l], label_offsets[l], num,overlap);
        if (logfp)
        {
          fprintf(logfp, "%s (%d): linear fit = %2.2f x + "
                  "%2.1f (%d voxels)\n",
                  cma_label_to_name(l), l,
                  label_scales[l], label_offsets[l],
                  num);
          fflush(logfp) ;
        }
        {
          HISTOlinearScale(h_gca, h_gca,
                           label_scales[l], label_offsets[l]) ;
          sprintf(fname, "%s_label%d_gca_scaled.plt", base_name, l) ;
          HISTOplot(h_gca, fname) ;
        }
      }
      else
      {
        printf("insufficient overlap %2.4f in "
               "%s histograms - rejecting\n",
               overlap, cma_label_to_name(l)) ;
      }

      if (l == Gdiag_no)
        DiagBreak() ;
      if (l >100)
        break ;
    }
    HISTOfree(&h_gca) ;
    HISTOfree(&h_mri) ;

    if (DIAG_VERBOSE_ON)
    {
      FILE *fp ;
      float scale, offset ;
      fp = fopen("norm_offset.plt", "r") ;
      for (l = 0 ; l < MAX_CMA_LABELS ; l++)
      {
        fscanf(fp, "%d %f %f", &l, &scale, &offset) ;
        label_scales[l] = scale ;
        label_offsets[l] = offset ;
        computed[l] = 1 ;
      }
      fclose(fp) ;
    }
    num = 0 ;
    mean_gm_scale = 0 ;
    mean_gm_offset = 0 ;
    fprintf(stdout, "not using caudate to estimate GM means\n") ;
    for (k = 0 ; k < NGM_LABELS ; k++)
    {
      label = gm_labels[k] ;
      if (computed[label])
      {
        mean_gm_scale += label_scales[label] ;
        mean_gm_offset += label_offsets[label] ;
        num++ ;
      }
    }
    if (num == 0)
    {
      mean_gm_scale = 1 ;
      mean_gm_offset = 0 ;
    }
    else
    {
      mean_gm_scale /= (float)num ;
      mean_gm_offset /= (float)num ;
    }

    num = 0 ;
    mean_wm_scale = 0 ;
    mean_wm_offset = 0 ;
    for (k = 0 ; k < NWM_LABELS ; k++)
    {
      label = wm_labels[k] ;
      if (computed[label])
      {
        mean_wm_scale += label_scales[label] ;
        mean_wm_offset += label_offsets[label] ;
        num++ ;
      }
    }
    if (num == 0)
    {
      mean_wm_scale = 1 ;
      mean_wm_offset = 0 ;
    }
    else
    {
      mean_wm_scale /= (float)num ;
      mean_wm_offset /= (float)num ;
    }

    num = 0 ;
    mean_csf_scale = 0 ;
    mean_csf_offset = 0 ;
    for (k = 0 ; k < NCSF_LABELS ; k++)
    {
      label = csf_labels[k] ;
      if (computed[label])
      {
        mean_csf_scale += label_scales[label] ;
        mean_csf_offset += label_offsets[label] ;
        num++ ;
      }
    }
    if (num == 0)
    {
      mean_csf_scale = 1 ;
      mean_csf_offset = 0 ;
    }
    else
    {
      mean_csf_scale /= (float)num ;
      mean_csf_offset /= (float)num ;
    }

    printf("estimating mean gm scale to be %2.2f x + %2.1f\n",
           mean_gm_scale, mean_gm_offset) ;
    printf("estimating mean wm scale to be %2.2f x + %2.1f\n",
           mean_wm_scale, mean_wm_offset) ;
    printf("estimating mean csf scale to be %2.2f x + %2.1f\n",
           mean_csf_scale, mean_csf_offset) ;

    // assume that cortical gm goes as wm
    if (computed[Left_Cerebral_Cortex] == 0 &&
        computed[Left_Cerebral_White_Matter] != 0)
    {
      if (m_by_label[Left_Cerebral_White_Matter])
        m_by_label[Left_Cerebral_Cortex] =
          MatrixCopy(m_by_label[Left_Cerebral_White_Matter], NULL) ;
      label_scales[Left_Cerebral_Cortex] = mean_gm_scale ;
      label_offsets[Left_Cerebral_Cortex] = mean_gm_offset ;
      computed[Left_Cerebral_Cortex] = 1;
    }
    if (computed[Right_Cerebral_Cortex] == 0 &&
        computed[Right_Cerebral_White_Matter] != 0)
    {
      if (m_by_label[Right_Cerebral_White_Matter])
        m_by_label[Right_Cerebral_Cortex] =
          MatrixCopy(m_by_label[Right_Cerebral_White_Matter], NULL) ;
      label_scales[Right_Cerebral_Cortex] = mean_gm_scale ;
      label_offsets[Right_Cerebral_Cortex] = mean_gm_offset ;
      computed[Right_Cerebral_Cortex] = 1;
    }

    // lock some labels scaling to others that have been estimated
    if (computed[Left_Caudate])
    {
      label_offsets[Left_Accumbens_area] = label_offsets[Left_Caudate] ;
      label_scales[Left_Accumbens_area] = label_scales[Left_Caudate] ;
      computed[Left_Accumbens_area] = 1;
    }
    if (computed[Right_Caudate])
    {
      label_offsets[Right_Accumbens_area] = label_offsets[Right_Caudate] ;
      label_scales[Right_Accumbens_area] = label_scales[Right_Caudate] ;
      computed[Right_Accumbens_area] = 1;
    }
    if (computed[Left_Inf_Lat_Vent] == 0 && computed[Left_Hippocampus] != 0)
    {
      label_scales[Left_Inf_Lat_Vent] = label_scales[Left_Hippocampus] ;
      label_offsets[Left_Inf_Lat_Vent] = label_offsets[Left_Hippocampus] ;
      computed[Left_Inf_Lat_Vent] = 1 ;
    }
    if (computed[Right_Inf_Lat_Vent] == 0 &&
        computed[Right_Hippocampus] != 0)
    {
      label_scales[Right_Inf_Lat_Vent] = label_scales[Right_Hippocampus] ;
      label_offsets[Right_Inf_Lat_Vent] =
        label_offsets[Right_Hippocampus] ;
      computed[Right_Inf_Lat_Vent] = 1 ;
    }

    label_scales[CSF] = mean_csf_scale ;
    label_scales[Fifth_Ventricle] = mean_csf_scale ;
    label_offsets[CSF] = mean_csf_offset ;
    label_offsets[Fifth_Ventricle] = mean_csf_offset ;
    computed[CSF] = computed[Fifth_Ventricle] = 1 ;


    if (logfp)
    {
      for (l = 0 ; l < MAX_CMA_LABELS ; l++)
        if (computed[l] != 0)
          fprintf(logfp, "label %s: scaling by %2.2f  + %2.1f\n",
                  cma_label_to_name(l),
                  label_scales[l], label_offsets[l]) ;
      fflush(logfp) ;
    }
    if (DIAG_VERBOSE_ON)
    {
      FILE *fp ;
      fp = fopen("norm_offset.plt", "w") ;
      for (l = 0 ; l < MAX_CMA_LABELS ; l++)
        if (computed[l] != 0)
          fprintf(fp, "%d %f %f\n", l, label_scales[l], label_offsets[l]) ;
      fclose(fp) ;
    }

    gcaCheck(gca) ;
    for (xn = 0 ; xn < gca->node_width ; xn++)
    {
      double     means_before[MAX_GCA_LABELS], \
      means_after[MAX_GCA_LABELS], scales[MAX_GCA_LABELS];
#if 1
      double     delta_i, delta_j ;
      int        xp, yp, zp ;
#endif
      int        labels[MAX_GCA_LABELS], niter ;
      LABEL_PROB ranks_before[MAX_GCA_LABELS], \
      ranks_after[MAX_GCA_LABELS] ;

      for (yn = 0 ; yn < gca->node_height ; yn++)
      {
        for (zn = 0 ; zn < gca->node_depth ; zn++)
        {
          if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z)
            DiagBreak() ;
          gcan = &gca->nodes[xn][yn][zn] ;
          if (gcan->nlabels <= 0)
            continue ;

          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            gc = &gcan->gcs[i] ;
            l = gcan->labels[i] ;
            labels[i] = l ;
            scales[i] = label_scales[l] ;
            means_before[i] = gc->means[frame] ;
            ranks_before[i].label = l ;
            ranks_before[i].prob = means_before[i] ;
            ranks_before[i].index = i ;
          }
          qsort(ranks_before, gcan->nlabels,
                sizeof(LABEL_PROB), compare_sort_probabilities) ;
          niter = 0 ;
          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            gc = &gcan->gcs[i] ;
            l = gcan->labels[i] ;
            means_after[i] =
              means_before[i]*label_scales[l] + label_offsets[l] ;
            if (means_after[i] < 0)
              means_after[i] = 0 ;
            ranks_after[i].label = l ;
            ranks_after[i].prob = means_after[i] ;
            ranks_after[i].index = i ;
          }
          qsort(ranks_after, gcan->nlabels,
                sizeof(LABEL_PROB), compare_sort_probabilities) ;
          for (i = 0 ; i < gcan->nlabels ; i++)
          {
#if 1
            if (ranks_before[i].label != ranks_after[i].label)
            {
              double    pi, pj, lambda ;
              int       j, ind_j, ind_i ;
              GCA_PRIOR *gcap;

              /* two have swapped position - put them */
              /* back in the right order */
              for (j = 0 ; j < gcan->nlabels ; j++)
                if (ranks_after[j].label == ranks_before[i].label)
                  break ;
              if (j >= gcan->nlabels)
              {
                DiagBreak() ;
                continue ;
              }
              gcaNodeToPrior(gca, xn, yn, zn, &xp, &yp, &zp) ;
              gcap = &gca->priors[xp][yp][zp] ;
              pi = getPrior(gcap, ranks_after[i].label) ;
              pj = getPrior(gcap, ranks_after[j].label) ;
              if (FZERO(pi) && FZERO(pj))
                break ;   // both labels will never happen
              lambda = pi / (pi + pj) ;
              ind_j = ranks_after[j].index ;
              ind_i = ranks_after[i].index ;
              delta_j =
                (means_after[ind_j] - means_after[ind_i]) *
                lambda ;
              delta_i =
                (means_after[ind_i] - means_after[ind_j]) *
                (1-lambda) ;

              if ((fabs(delta_j) < 1) && (fabs(delta_i) < 1))
              {
                // this will move one mean to the
                // other side of the other
                if ((fabs(delta_j) > fabs(delta_i)) &&
                    !FZERO(delta_j))
                  delta_j /= fabs(delta_j) ;  // make it +-1
                else if (!FZERO(delta_i))
                  delta_i /= fabs(delta_i) ;  // make it +-1
              }
              if (!finite(delta_i) || !finite(delta_j))
              {
                DiagBreak() ;
                break ;
              }
              ranks_after[j].prob =
                means_after[ind_j] = means_after[ind_j] - delta_j ;
              ranks_after[i].prob =
                means_after[ind_i] = means_after[ind_i] - delta_i ;
              if ((xn == Gx && yn == Gy && zn == Gz) &&
                  (ranks_after[i].label == gcan->labels[i] ||
                   ranks_after[j].label == gcan->labels[j] ||
                   Ggca_label < 0))
              {
                printf("ordering of labels %s and %s changed, "
                       "modifying means by %2.0f (%2.1f) "
                       "and %2.0f (%2.1f)\n",
                       cma_label_to_name(ranks_after[i].label),
                       cma_label_to_name(ranks_after[j].label),
                       means_after[i], delta_i,
                       means_after[j], delta_i) ;
              }

              qsort(ranks_after, gcan->nlabels,
                    sizeof(LABEL_PROB),
                    compare_sort_probabilities) ;
              i = -1 ;   /* start loop over */
              if (niter++ > 9)
              {
                DiagBreak() ;
                break ;
              }
              continue ;
            }
#endif
          }

          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            if (FZERO(label_scales[gcan->labels[i]]))
              continue ;
            gc = &gcan->gcs[i] ;
            if ((xn == Gx && yn == Gy && zn == Gz) &&
                (Ggca_label == gcan->labels[i] || Ggca_label < 0))
            {
              printf("scaling gc for label %s at "
                     "(%d, %d, %d) from %2.1f to %2.1f\n",
                     cma_label_to_name(gcan->labels[i]),
                     xn, yn, zn,
                     means_before[i], means_after[i]) ;
              DiagBreak() ;
            }
            gc->means[frame] = means_after[i] ;
            check_finite("after rescaling", gc->means[frame]) ;
          }
        }
      }
    }
    gcaCheck(gca) ;
  }

  if (plta)  // return linear transform array to caller
  {
    int i ;

    // count # of xforms
    for (i = l = 0 ; l < MAX_CMA_LABELS ; l++)
    {
      if (m_by_label[l] != NULL)
        i++ ;
    }

    if (i > 0)  // should always be true
    {
      *plta = lta = LTAalloc(i, mri) ;
      for (i = l = 0 ; l < MAX_CMA_LABELS ; l++)
      {
        if (m_by_label[l] != NULL)
        {
          MatrixCopy(m_by_label[l], lta->xforms[i].m_L) ;
          MatrixFree(&m_by_label[l]) ;
          lta->xforms[i].label = l ;
          i++ ;
        }
      }
    }
  }

  if (mri_seg)
    MRIfree(&mri_seg) ;
  return(NO_ERROR) ;
}
#endif

static int lh_labels[] =
  {
    Left_Cerebral_White_Matter,
    Left_Hippocampus,
    Left_Cerebral_Cortex,
    Left_Lateral_Ventricle,
    Left_Caudate,
    Left_Cerebellum_Cortex,
    Left_Cerebellum_White_Matter,
    Left_Amygdala,
    Left_Thalamus_Proper,
    Left_Putamen,
    Left_Pallidum,
    Left_VentralDC,
  } ;
static int rh_labels[] =
  {
    Right_Cerebral_White_Matter,
    Right_Hippocampus,
    Right_Cerebral_Cortex,
    Right_Lateral_Ventricle,
    Right_Caudate,
    Right_Cerebellum_Cortex,
    Right_Cerebellum_White_Matter,
    Right_Amygdala,
    Right_Thalamus_Proper,
    Right_Putamen,
    Right_Pallidum,
    Right_VentralDC,
  } ;

#define NHEMI_LABELS (sizeof(rh_labels) / sizeof(rh_labels[0]))
int
GCAmapRenormalizeWithAlignment(GCA *gca,
                               MRI *mri,
                               TRANSFORM *transform,
                               FILE *logfp,
                               char *base_name,
                               LTA **plta,
                               int handle_expanded_ventricles)
{
  HISTOGRAM *h_mri, *h_gca ;
  int       l, nbins, i, x, y, z, xn, yn, zn, num, frame, \
  bin, j, n, computed[MAX_CMA_LABELS], b, label, k,
  border = BORDER_SIZE, gca_peak, mri_peak ;
  float     fmin, fmax, label_scales[MAX_CMA_LABELS], overlap,
  mean_gm_scale, mean_wm_scale, mean_csf_scale, label_peaks[MAX_CMA_LABELS],
  label_offsets[MAX_CMA_LABELS], \
  mean_wm_offset, mean_csf_offset, mean_gm_offset,
  lower_thresh, upper_thresh ;
  Real      val/*, scale*/ ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  MRI       *mri_seg = NULL, *mri_aligned, *mri_labels = NULL ;
  char      fname[STRLEN] ;
  MATRIX    *m_L, *m_by_label[MAX_CMA_LABELS] ;
  LTA       *lta ;

  double    det = -1 ;
  float peak_threshold = 0.03;
  float overlap_threshold = 0.001;
  int equiv_class[MAX_CMA_LABELS];

  if (transform->type == MORPH_3D_TYPE)
  {
    peak_threshold = 0.01;
    overlap_threshold = -1.0; //at mri_ca_label stage;
    // trust the registration more
  }

  set_equilavent_classes(equiv_class);

  printf("renormalizing by structure alignment....\n") ;
  if (plta)
    lta = *plta ;
  else
    lta = NULL ;
  for (frame = 0 ; frame < mri->nframes ; frame++)
  {
    for (l = 0 ; l < MAX_CMA_LABELS ; l++)
    {
      if (l == Gdiag_no)
        DiagBreak() ;
      label_scales[l] = 1.0 ;
      label_offsets[l] = 0.0 ;
      computed[l] = 0 ;
      m_by_label[l] = NULL ;  // not estimated yet
    }

    printf("renormalizing input #%d\n", frame) ;
    MRIvalRangeFrame(mri, &fmin, &fmax, frame) ;
    nbins = 256 ;
    h_mri = HISTOalloc(nbins) ;
    for (j = 0 ; j < NALIGN_LABELS ; j++)
    {
      l = align_labels[j] ;
      if (l == Gdiag_no)
        DiagBreak() ;


      mri_seg = MRIclone(mri, mri_seg) ;
      mri_labels = MRIclone(mri, mri_labels) ;

      /* include 2 voxel border to get context around structure.
        e.g. hippo is made easier to find by wm inferior and ventricle
        posterior.
      */
      if (transform->type != MORPH_3D_TYPE)
      {
        if (IS_HIPPO(l) ||
            IS_AMYGDALA(l) ||
            IS_CAUDATE(l) ||
            IS_PUTAMEN(l) ||
            IS_PALLIDUM(l))
          border = BORDER_SIZE+1 ;  // need more context for hippo
        else if (IS_GM(l))
          border = 0;
        else if (IS_WHITE_CLASS(l))
          border = WM_BORDER_SIZE ;
        else
          border = BORDER_SIZE ;
        GCAbuildMostLikelyVolumeForStructure
          (gca, mri_seg, l, border, transform,mri_labels) ;
        for (x = 0 ; x < mri_labels->width ; x++)
        {
          for (y = 0 ; y < mri_labels->height ; y++)
          {
            for (z = 0 ; z < mri_labels->depth ; z++)
            {
              if (x == Gx && y == Gy && z == Gz)
                DiagBreak() ;
              label = MRIgetVoxVal(mri_labels, x, y, z, 0) ;
              if (computed[label] == 0)
                continue ;
              val = MRIgetVoxVal(mri_seg, x, y, z, frame) ;
              val = val * label_scales[label] +
                    label_offsets[label] ;
              MRIsetVoxVal(mri_seg, x, y, z, frame, val) ;
            }
          }
        }

        /* ventricle at the posterior part of hippo
          frequently makes local minima
          in alignment energy functional - remove them.
        */
        if (l == Left_Hippocampus || l == Right_Hippocampus)
        {
          for (x = 0 ; x < mri_labels->width ; x++)
          {
            for (y = 0 ; y < mri_labels->height ; y++)
            {
              for (z = 0 ; z < mri_labels->depth ; z++)
              {
                if (x == Gx && y == Gy && z == Gz)
                  DiagBreak() ;
                label = MRIgetVoxVal(mri_labels, x, y, z, 0) ;
                if (IS_LAT_VENT(label) || IS_INF_LAT_VENT(label))
                  MRIsetVoxVal(mri_seg, x, y, z, frame, 0) ;
              }
            }
          }
        }
        if (l == Left_Cerebral_White_Matter ||
            l == Right_Cerebral_White_Matter)
        {
          MRI *mri_tmp, *mri_border ;

          // create a volume that is the wm eroded 3 times,
          // plus the border voxels
          mri_tmp = MRIclone(mri_seg, NULL) ;
          GCAbuildMostLikelyVolumeForStructure
            (gca, mri_tmp, l, BORDER_SIZE, transform, NULL) ;
          mri_border = MRIsubtract(mri_seg, mri_tmp, NULL) ;
          // just outside

          // erode just the interior 4 times to get to high prob regions
          MRIerode(mri_tmp, mri_tmp) ;
          MRIerode(mri_tmp, mri_tmp) ;
          MRIerode(mri_tmp, mri_tmp) ;
          MRIerode(mri_tmp, mri_tmp) ;
          MRIerode(mri_tmp, mri_tmp) ;
          MRIerode(mri_tmp, mri_tmp) ; // two more to remove border
          MRIadd(mri_tmp, mri_border, mri_seg) ;  // border + interior
          MRIfree(&mri_tmp) ;
          MRIfree(&mri_border) ;
        }

        if (Gdiag & DIAG_WRITE)
        {
          sprintf(fname, "%s_label%d.mgz", base_name, l) ;
          MRIwrite(mri_seg, fname) ;
        }

        if (lta)   // try to find a previously computed one
        {
          for (n = 0 ; n < lta->num_xforms ; n++)
            if (lta->xforms[n].label == l)
              break ;
          if (n >= lta->num_xforms)
            n = -1 ;  // indicate no xform found
        }
        else   // no transform specified by caller
          n = -1 ;
        if (n < 0)  // no transform - compute one
        {
          // float  evalues[4] ;
          // MATRIX *m_evectors ;

          printf("aligning %s...\n", cma_label_to_name(l)) ;
          m_L = MRIgetVoxelToVoxelXform(mri_seg, mri) ;
          if (! IS_GM(l))
          { // will use alignment of WM for GM
            //  MRIpowellAlignImages(mri_seg, mri,
            // m_L, &scale_factor, NULL) ;
            if ((l == Left_Lateral_Ventricle ||
                 l == Right_Lateral_Ventricle) &&
                (transform->type != MORPH_3D_TYPE) &&
                (handle_expanded_ventricles == 1))
            {
              char label_base_name[STRLEN] ;
              sprintf(label_base_name, "%s_label%d", base_name, l) ;
              initialize_ventricle_alignment
                (mri_seg, mri, m_L, label_base_name) ;
            }
            MRIfaridAlignImages(mri_seg, mri,  m_L) ;
          }
          else
          {
            // assume that cortical gm goes as wm
            if ((l == Left_Cerebral_Cortex) &&
                computed[Left_Cerebral_White_Matter] != 0)
            {
              if (m_by_label[Left_Cerebral_White_Matter])
                m_L =
                  MatrixCopy(m_by_label[Left_Cerebral_White_Matter], m_L) ;
            }
            if ( (l == Right_Cerebral_Cortex) &&
                 computed[Right_Cerebral_White_Matter] != 0)
            {
              if (m_by_label[Right_Cerebral_White_Matter])
                m_L =
                  MatrixCopy(m_by_label[Right_Cerebral_White_Matter], m_L) ;
            }
          }

          det = MatrixDeterminant(m_L) ;
          if (det > 4 && det < 8 &&
              (l == Left_Lateral_Ventricle || l == Right_Lateral_Ventricle))
            det = 1 ;  // allow large determinants for the ventricles

#if 0
          // for non-ventricular structures make sure that
          // there isn't too much shape deformation
          if (l != Left_Lateral_Ventricle && l != Right_Lateral_Ventricle)
          {
            float evalues[4], cond ;
            MATRIX *m ;

            m = MatrixCopyRegion(m_L, NULL, 1, 1, 3, 3, 1, 1) ;
            cond = MatrixSVDEigenValues(m, evalues) ;
            MatrixFree(&m) ;

            if (evalues[0]/evalues[2] > 2)
            {
              printf("extreme anistropy detected in transform "
                     "(%2.2f=%2.2f/%2.2f)\n", 
                     evalues[0]/evalues[2], evalues[0], evalues[2]) ;
              det = -1 ;
            }
          }
#endif

          printf("det = %2.3f, M=\n", det) ;
          MatrixPrint(stdout, m_L) ;
          if (det < 0.25 || det > 4)
          {
            printf("invalid transform detected (det=%2.4f) \n",det) ;
            det = -1 ;  // mark it as invalid for later
            MatrixFree(&m_L) ;
            m_L = MRIgetVoxelToVoxelXform(mri_seg, mri) ;
          }
        }
        else   // use previously computed transform
          m_L = MatrixCopy(lta->xforms[n].m_L, NULL) ;

        if (l == Gdiag_no)
          DiagBreak() ;

        if (Gdiag & DIAG_WRITE)
        {
          sprintf(fname, "%s_label%d_after.mgz", base_name, l) ;
          mri_aligned = MRIlinearTransform(mri_seg, NULL, m_L) ;
          MRIwrite(mri_aligned, fname) ;
          MRIfree(&mri_aligned) ;
        }

        if (l == Left_Cerebral_White_Matter ||
            l == Right_Cerebral_White_Matter)
        {
          // wm so big it's hard to localize with a linear xform
          GCAbuildMostLikelyVolumeForStructure
          (gca, mri_seg, l, 0, transform, NULL) ;
          MRIerode(mri_seg, mri_seg) ;
          MRIerode(mri_seg, mri_seg) ;
        }
        else
        {
          /* put ventricles back in for erosion to remove
            (otherwise a bunch of hippo
            gets removed */
          if (l == Left_Hippocampus || l == Right_Hippocampus)
          {
            for (x = 0 ; x < mri_labels->width ; x++)
            {
              for (y = 0 ; y < mri_labels->height ; y++)
              {
                for (z = 0 ; z < mri_labels->depth ; z++)
                {
                  if (x == Gx && y == Gy && z == Gz)
                    DiagBreak() ;
                  label = MRIgetVoxVal(mri_labels, x, y, z, 0) ;
                  if (IS_LAT_VENT(label) ||
                      IS_INF_LAT_VENT(label))
                    MRIsetVoxVal(mri_seg, x, y, z, frame, 128) ;
                }
              }
            }
          }
          for (b = 0 ; b < border ; b++)
            MRIerode(mri_seg, mri_seg) ; // get rid of outside border
          MRIerode(mri_seg, mri_seg) ; // get rid of inside border
        }

        mri_aligned = MRIlinearTransform(mri_seg, NULL, m_L) ;
      }
      else  // 3d morph already done - don't bother aligning
      {
        m_L = NULL ;
        if (l == Left_Cerebral_White_Matter ||
            l == Right_Cerebral_White_Matter)
        {
          // wm so big it's hard to localize with a linear xform
          GCAbuildMostLikelyVolumeForStructure
          (gca, mri_seg, l, 0, transform, NULL) ;
          MRIerode(mri_seg, mri_seg) ;
        }
        else
        {
          GCAbuildMostLikelyVolumeForStructure
          (gca, mri_seg, l, 0, transform, NULL) ;
        }
        mri_aligned = MRIerode(mri_seg, NULL);
      }

      MRIbinarize(mri_aligned, mri_aligned, 1, 0, 128) ;
      if (Gdiag & DIAG_WRITE)
      {
        sprintf(fname, "%s_label%d_eroded.mgz", base_name, l) ;
        MRIwrite(mri_aligned, fname) ;
      }
      if (l == Gdiag_no)
        DiagBreak() ;
      HISTOclear(h_mri, h_mri) ;
      h_mri->bin_size = (fmax-fmin)/255.0 ;
      if (h_mri->bin_size < 1 &&
          (mri->type == MRI_UCHAR || mri->type == MRI_SHORT))
        h_mri->bin_size = 1 ;
      for (i = 0 ; i < nbins ; i++)
        h_mri->bins[i] = (i+1)*h_mri->bin_size ;

      for (num = x = 0 ; x < mri_aligned->width ; x++)
      {
        for (y = 0 ; y < mri_aligned->height ; y++)
        {
          for (z = 0 ; z < mri_aligned->depth ; z++)
          {
            if (x == Gx && y == Gy && z == Gz)
              DiagBreak() ;
            MRIsampleVolumeFrame(mri_aligned, x, y, z, frame, &val) ;
            if (DZERO(val))  // not in this structure
              continue ;
            MRIsampleVolumeFrame(mri, x, y, z, frame, &val) ;

            if (FZERO(val))  // skull stripped
              continue ;
            bin = nint((val - fmin)/h_mri->bin_size) ;
            if (bin >= h_mri->nbins)
              bin = h_mri->nbins-1 ;
            else if (bin < 0)
              bin = 0 ;

            h_mri->counts[bin]++ ;
            num++ ;
          }
        }
      }
      MRIfree(&mri_aligned) ;

      h_gca = gcaGetLabelHistogram(gca, l, 0) ;
      gca_peak = HISTOfindHighestPeakInRegion(h_gca, 0, h_gca->nbins) ;
      HISTOmakePDF(h_gca, h_gca) ;
      if (gca_peak >= 0)
        printf("gca peak = %2.5f (%2.0f)\n", 
               h_gca->counts[gca_peak], h_gca->bins[gca_peak]) ;
      label_peaks[l] = h_gca->bins[gca_peak] ;
      fflush(stdout);

      mri_peak = HISTOfindHighestPeakInRegion(h_mri, 0, h_mri->nbins) ;
      HISTOfillHoles(h_mri) ;
      HISTOmakePDF(h_mri, h_mri) ;
      if (mri_peak >= 0)
        printf("mri peak = %2.5f (%2.0f)\n", 
               h_mri->counts[mri_peak], h_mri->bins[mri_peak]) ;
      fflush(stdout);

      if (IS_CSF(l) && h_mri->bins[mri_peak] > 55)
      {
        printf("CSF peak too bright - rejecting\n") ;
        continue ;
      }
      if (h_mri->counts[mri_peak] < peak_threshold || num <= 50)
        /* not enough to reliably estimate density */
      {
        if (h_mri->counts[mri_peak] < peak_threshold)
          printf("uniform distribution in MR - "
                 "rejecting arbitrary fit\n") ;
        if (m_L)
          MatrixFree(&m_L) ;
        continue ;
      }
      if (m_L)
      {
        if (plta && (!IS_GM(l))) //GM will be copied from WM later
          m_by_label[l] = m_L ;  // store if for assembling an LTA later
        else
          MatrixFree(&m_L) ;
      }

      if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
      {
        sprintf(fname, "%s_label%d_mri.plt", base_name, l) ;
        HISTOplot(h_mri, fname) ;
        sprintf(fname, "%s_label%d_gca.plt", base_name, l) ;
        HISTOplot(h_gca, fname) ;
        DiagBreak() ;
      }
      overlap = HISTOthreshSum(h_mri, h_gca, .025) ;

      //if (overlap > 0.01)
      //      if (overlap > 0.001)
      if (IS_LAT_VENT(l) || overlap > overlap_threshold)
      {
        //                        if (l == Gdiag_no)
        //  HISTOfindLinearFit(h_gca, h_mri, .025, 10, -75, 75,
        // &label_scales[l],  &label_offsets[l]) ;
        //              HISTOfindLinearFit(h_gca, h_mri, .025,
        // 4, -125, 125, &label_scales[l], &label_offsets[l]) ;
        HISTOfindLinearFit(h_gca, h_mri, .025, 4, 0, 0,
                           &label_scales[l], &label_offsets[l]) ;

        val = h_gca->bins[gca_peak]*label_scales[l]+label_offsets[l] ;
        switch (l)
        {
        case Brain_Stem:
        case Left_VentralDC:
        case Right_VentralDC:
          lower_thresh = 80 ;
          upper_thresh = 110 ;
          break ;
        case Left_Caudate:
        case Right_Caudate:
          lower_thresh = 50 ;
          upper_thresh = 100 ;
          break ;
        case Left_Cerebral_Cortex:
        case Right_Cerebral_Cortex:
          lower_thresh = 40 ;
          upper_thresh = 95 ;
          break ;
        case Left_Pallidum:
        case Right_Pallidum:
          lower_thresh = 75 ;
          upper_thresh = 135 ;
          break ;
        case Left_Thalamus_Proper:
        case Right_Thalamus_Proper:
          lower_thresh = 75 ;
          upper_thresh = 120 ;
          break ;
        case Left_Cerebral_White_Matter:
        case Right_Cerebral_White_Matter:
          lower_thresh = 90 ;
          upper_thresh = 130 ;
          break ;
        case Left_Putamen:
        case Right_Putamen:
          lower_thresh = 60 ;
          upper_thresh = 100 ;
          break ;
        case Left_Lateral_Ventricle:
        case Right_Lateral_Ventricle:
        case Third_Ventricle:
        case Fourth_Ventricle:
        case CSF:
          lower_thresh = 0 ;
          upper_thresh = 40 ;
          break ;
        case Left_Inf_Lat_Vent:
        case Right_Inf_Lat_Vent:
          lower_thresh = 0 ;
          upper_thresh = 65 ;
          break ;
        default:
          lower_thresh = 0 ;
          upper_thresh = 256 ;
          break ;
        }
        if ((val < lower_thresh || 
             val > upper_thresh) ||
            (h_mri->bins[mri_peak] < lower_thresh || 
             h_mri->bins[mri_peak] > upper_thresh))
        {
          //          if (transform->type != MORPH_3D_TYPE)
          {
            printf("unreasonable value (%2.1f/%2.1f), "
                   "not in range [%2.0f, %2.0f] - rejecting\n",
                   val, h_mri->bins[mri_peak], lower_thresh, upper_thresh) ;
            label_scales[l] = 1.0 ;
            label_offsets[l] = 1.0 ;
            continue ;
          }
        }

        // only allow certain labels to be used for initializing the 3d morph
        // (which is what happens when computed[l] = 2)
        computed[l] = (det > 0) ? 2 : 1 ;
        printf("%s (%d): linear fit = %2.2f x + %2.1f "
               "(%d voxels, overlap=%2.3f)\n",
               cma_label_to_name(l), l,
               label_scales[l], label_offsets[l], num,overlap);

        //note that the following range need be changed
        // if both scale and offset are allowed' 1/1.5 = 0.67
        if (IS_LAT_VENT(l))
        {
          if (label_scales[l] < 0.4) label_scales[l] = 0.4;
          else if (label_scales[l] > 1.5) label_scales[l] = 1.5;
        }
        if ((label_scales[l] < 0.67 ||
             (label_scales[l] > 1.5)) && !IS_LAT_VENT(l))
        {
          /*
           if(IS_CSF(l)){
           if(label_scales[l] < 0.67) label_scales[l] = 0.67;
           else if(label_scales[l] > 1.5) label_scales[l] = 1.5;
           } else
          */
          {
            //scaling is unreliable, ignore it
            computed[l] = 0 ;
            m_by_label[l] = NULL;
          }
        }
        label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];

        if (logfp)
        {
          fprintf(logfp, "%s (%d): linear fit = %2.2f x + "
                  "%2.1f (%d voxels, peak = %2.0f), gca=%2.1f\n",
                  cma_label_to_name(l), l,
                  label_scales[l], label_offsets[l],
                  num, val, label_peaks[l]);
          fflush(logfp) ;
        }
        fprintf(stdout, "%s (%d): linear fit = %2.2f x + "
                "%2.1f (%d voxels, peak = %2.0f), gca=%2.1f\n",
                cma_label_to_name(l), l,
                label_scales[l], label_offsets[l],
                num, val, label_peaks[l]);
        fflush(stdout) ;
        {
          HISTOlinearScale(h_gca, h_gca,
                           label_scales[l], label_offsets[l]) ;
          if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
          {
            sprintf(fname, "%s_label%d_gca_scaled.plt", base_name, l) ;
            HISTOplot(h_gca, fname) ;
          }
        }
      }
      else
      {
        printf("overlap = %g, overlap_threshold = %g\n",
               overlap, overlap_threshold);
        printf("insufficient overlap %2.4f in histograms - rejecting\n",
               overlap) ;
      }

      if (l == Gdiag_no)
        DiagBreak() ;
      if (l >100)
        break ;
    }
    HISTOfree(&h_gca) ;
    HISTOfree(&h_mri) ;

    // make sure non-computed labels don't scale
    for (l = 0 ; l < MAX_CMA_LABELS ; l++)
    {
      if (computed[l] == 0)
      {
        label_scales[l] = 1.0 ;
        label_offsets[l] = 0.0 ;
        h_gca = gcaGetLabelHistogram(gca, l, 0) ;
        gca_peak = HISTOfindHighestPeakInRegion(h_gca, 0, h_gca->nbins) ;
        HISTOmakePDF(h_gca, h_gca) ;
        if (gca_peak >= 0)
          printf("gca peak %s = %2.5f (%2.0f)\n", 
                 cma_label_to_name(l), 
                 h_gca->counts[gca_peak], 
                 h_gca->bins[gca_peak]) ;
        label_peaks[l] = h_gca->bins[gca_peak] ;
        fflush(stdout);
      }
    }

    if (DIAG_VERBOSE_ON)
    {
      FILE *fp ;
      float scale, offset ;
      fp = fopen("norm_offset.plt", "r") ;
      if (fp != NULL)
      {
        for (l = 0 ; l < MAX_CMA_LABELS ; l++)
        {
          fscanf(fp, "%d %f %f", &l, &scale, &offset) ;
          label_scales[l] = scale ;
          label_offsets[l] = offset ;
          computed[l] = 1 ;
        }
        fclose(fp) ;
      }
    }
    fprintf(stdout, "not using caudate to estimate GM means\n") ;
    for (k = 0 ; k < NHEMI_LABELS ; k++)
    {
      int lhl, rhl ;
      if (computed[lh_labels[k]] && !computed[rh_labels[k]])
      {
        lhl = lh_labels[k] ;
        rhl = rh_labels[k] ;
        label_scales[rhl] = label_scales[lhl] ;
        label_offsets[rhl] = label_offsets[lhl] ;
        label_peaks[rhl] = label_peaks[lhl] ;
        computed[rhl] = 1;
        fprintf(stdout, "setting label %s based on %s = %2.2f x + %2.0f\n",
                cma_label_to_name(lhl), cma_label_to_name(rhl),
                label_scales[rhl], label_offsets[rhl]) ;
      }
      else if (computed[rh_labels[k]] && !computed[lh_labels[k]])
      {
        lhl = lh_labels[k] ;
        rhl = rh_labels[k] ;
        label_scales[lhl] = label_scales[rhl] ;
        label_offsets[lhl] = label_offsets[rhl] ;
        label_peaks[lhl] = label_peaks[rhl] ;
        computed[lhl] = 1;
        fprintf(stdout, "setting label %s based on %s = %2.2f x + %2.0f\n",
                cma_label_to_name(rhl), cma_label_to_name(lhl),
                label_scales[lhl], label_offsets[lhl]) ;
      }
    }

    num = 0 ;
    mean_gm_scale = 0 ;
    mean_gm_offset = 0 ;
    for (k = 0 ; k < NGM_LABELS ; k++)
    {
      label = gm_labels[k] ;
      if (computed[label])
      {
        mean_gm_scale += label_scales[label] ;
        mean_gm_offset += label_offsets[label] ;
        num++ ;
      }
    }
    if (num == 0)
    {
      mean_gm_scale = 1 ;
      mean_gm_offset = 0 ;
    }
    else
    {
      mean_gm_scale /= (float)num ;
      mean_gm_offset /= (float)num ;
    }

    num = 0 ;
    mean_wm_scale = 0 ;
    mean_wm_offset = 0 ;
    for (k = 0 ; k < NWM_LABELS ; k++)
    {
      label = wm_labels[k] ;
      if (computed[label])
      {
        mean_wm_scale += label_scales[label] ;
        mean_wm_offset += label_offsets[label] ;
        num++ ;
      }
    }
    if (num == 0)
    {
      mean_wm_scale = 1 ;
      mean_wm_offset = 0 ;
    }
    else
    {
      mean_wm_scale /= (float)num ;
      mean_wm_offset /= (float)num ;
    }

    num = 0 ;
    mean_csf_scale = 0 ;
    mean_csf_offset = 0 ;
    for (k = 0 ; k < NCSF_LABELS ; k++)
    {
      label = csf_labels[k] ;
      if (computed[label])
      {
        mean_csf_scale += label_scales[label] ;
        mean_csf_offset += label_offsets[label] ;
        num++ ;
      }
    }
    if (num == 0)
    {
      mean_csf_scale = 1 ;
      mean_csf_offset = 0 ;
    }
    else
    {
      mean_csf_scale /= (float)num ;
      mean_csf_offset /= (float)num ;
    }

    printf("estimating mean gm scale to be %2.2f x + %2.1f\n",
           mean_gm_scale, mean_gm_offset) ;
    printf("estimating mean wm scale to be %2.2f x + %2.1f\n",
           mean_wm_scale, mean_wm_offset) ;
    printf("estimating mean csf scale to be %2.2f x + %2.1f\n",
           mean_csf_scale, mean_csf_offset) ;

    // assume that cortical gm goes as wm
    if (computed[Left_Cerebral_Cortex] == 0 &&
        computed[Left_Cerebral_White_Matter] != 0)
    {
      if (m_by_label[Left_Cerebral_White_Matter])
        m_by_label[Left_Cerebral_Cortex] =
          MatrixCopy(m_by_label[Left_Cerebral_White_Matter], NULL) ;
      label_scales[Left_Cerebral_Cortex] = mean_gm_scale ;
      label_offsets[Left_Cerebral_Cortex] = mean_gm_offset ;
      computed[Left_Cerebral_Cortex] = 1;
      l = Left_Cerebral_Cortex ;
      label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];
    }
    if (computed[Left_Cerebellum_Cortex] == 0)
    {
      label_scales[Left_Cerebellum_Cortex] = mean_gm_scale ;
      label_offsets[Left_Cerebellum_Cortex] = mean_gm_offset ;
      computed[Left_Cerebellum_Cortex] = 1;
      l = Left_Cerebellum_Cortex ;
      label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];
      printf("setting left cbm cortex = %2.2f x + %2.2f\n",
             mean_gm_scale, mean_gm_offset) ;
    }
    if (computed[Right_Cerebellum_Cortex] == 0)
    {
      label_scales[Right_Cerebellum_Cortex] = mean_gm_scale ;
      label_offsets[Right_Cerebellum_Cortex] = mean_gm_offset ;
      computed[Right_Cerebellum_Cortex] = 1;
      l = Right_Cerebellum_Cortex ;
      label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];
      printf("setting right cbm cortex = %2.2f x + %2.2f\n",
             mean_gm_scale, mean_gm_offset) ;
    }
    if (computed[Right_Cerebral_Cortex] == 0 &&
        computed[Right_Cerebral_White_Matter] != 0)
    {
      if (m_by_label[Right_Cerebral_White_Matter])
        m_by_label[Right_Cerebral_Cortex] =
          MatrixCopy(m_by_label[Right_Cerebral_White_Matter], NULL) ;
      label_scales[Right_Cerebral_Cortex] = mean_gm_scale ;
      label_offsets[Right_Cerebral_Cortex] = mean_gm_offset ;
      l = Right_Cerebral_Cortex ;
      label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];
      computed[Right_Cerebral_Cortex] = 1;
    }

    // lock some labels scaling to others that have been estimated
    if (computed[Left_Caudate])
    {
      label_offsets[Left_Accumbens_area] = label_offsets[Left_Caudate] ;
      label_scales[Left_Accumbens_area] = label_scales[Left_Caudate] ;
      l = Left_Accumbens_area ;
      label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];
      computed[Left_Accumbens_area] = 1;
    }
    if (computed[Right_Caudate])
    {
      label_offsets[Right_Accumbens_area] = label_offsets[Right_Caudate] ;
      label_scales[Right_Accumbens_area] = label_scales[Right_Caudate] ;
      l = Right_Accumbens_area ;
      label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];
      computed[Right_Accumbens_area] = 1;
    }
    if (computed[Left_Inf_Lat_Vent] == 0 && computed[Left_Hippocampus] != 0)
    {
      label_scales[Left_Inf_Lat_Vent] = label_scales[Left_Hippocampus] ;
      label_offsets[Left_Inf_Lat_Vent] = label_offsets[Left_Hippocampus] ;
      l = Left_Inf_Lat_Vent ;
      label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];
      computed[Left_Inf_Lat_Vent] = 1 ;
    }
    if (computed[Right_Inf_Lat_Vent] == 0 &&
        computed[Right_Hippocampus] != 0)
    {
      label_scales[Right_Inf_Lat_Vent] = label_scales[Right_Hippocampus] ;
      label_offsets[Right_Inf_Lat_Vent] =
        label_offsets[Right_Hippocampus] ;
      l = Right_Inf_Lat_Vent ;
      label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];
      computed[Right_Inf_Lat_Vent] = 1 ;
    }

    label_scales[CSF] = mean_csf_scale ;
    label_scales[Fifth_Ventricle] = mean_csf_scale ;
    label_offsets[CSF] = mean_csf_offset ;
    label_offsets[Fifth_Ventricle] = mean_csf_offset ;
    computed[CSF] = computed[Fifth_Ventricle] = 1 ;
    l = CSF ;
    label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];
    l = Fifth_Ventricle ;
    label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];

    //set the scale and offset for the rest; added by xhan
    for (l = 0 ; l < MAX_CMA_LABELS ; l++)
    {
      if (l == Gdiag_no)
        DiagBreak() ;
      if (computed[l] > 0)
        continue;
      if (equiv_class[l] == 1)
      {
        label_scales[l] = mean_csf_scale;
        label_offsets[l] = mean_csf_offset;
        computed[l] = 1 ;
      }
      else if (equiv_class[l] == 2)
      {
        label_scales[l] = mean_wm_scale;
        label_offsets[l] = mean_wm_offset;
        computed[l] = 1 ;
      }
      else if (equiv_class[l] == 3)
      {
        label_scales[l] = mean_gm_scale;
        label_offsets[l] = mean_gm_offset;
        computed[l] = 1 ;
      }
      label_peaks[l] = label_peaks[l] * label_scales[l] + label_offsets[l];
    }

    // make sure labels are self-consistent
    for (l = 0 ; l < MAX_CMA_LABELS ; l++)
    {
      double peak, scale ;
      switch (l)
      {
      case Left_Pallidum:
      case Right_Pallidum:
#if 1
        if ((label_peaks[l] >= .95*label_peaks[Left_Cerebral_White_Matter]) ||
            (label_peaks[l] >= .95*label_peaks[Right_Cerebral_White_Matter]))
        {
          // don't let pallidum be as bright as wm
          peak = 0.95 *
                 (label_peaks[Left_Cerebral_White_Matter] +
                  label_peaks[Right_Cerebral_White_Matter])/2 ;
          scale = peak / label_peaks[l] ;
          printf("%s too bright - rescaling by %2.3f "
                 "(from %2.3f) to %2.1f (was %2.1f)\n",
                 cma_label_to_name(l),
                 scale, label_scales[l], peak, label_peaks[l]) ;
          label_scales[l] = scale ;
          label_peaks[l] = peak ;
        }
#endif
        break ;
      case Left_Putamen:
      case Right_Putamen:
        if ((label_peaks[l] >= .9*label_peaks[Left_Cerebral_White_Matter]) ||
            (label_peaks[l] >= .9*label_peaks[Right_Cerebral_White_Matter]))
        {
          // don't let putamen be as bright as wm
          peak = 0.9 *
                 (label_peaks[Left_Cerebral_White_Matter] +
                  label_peaks[Right_Cerebral_White_Matter])/2 ;
          scale = peak / label_peaks[l] ;
          printf("%s too bright - rescaling by %2.3f "
                 "(from %2.3f) to %2.1f (was %2.1f)\n",
                 cma_label_to_name(l),
                 scale, label_scales[l], peak, label_peaks[l]) ;
          label_scales[l] = scale ;
          label_peaks[l] = peak ;
        }
        break ;
      }
    }

    // now use precomputed eigenstructure of intensity 
    // volumes to adjust corrections
#if 0
    {
#define NUM_EIG 5
      MATRIX *m_eig ;
      VECTOR *v_dot, *v_obs, *v_obs_estimated, *v_dot_T ;
      int     r, c, c2, num_eig = NUM_EIG ;

      m_eig = MatrixAlloc(NUM_INT_EIG_LABELS, num_eig, MATRIX_REAL) ;
      v_obs = RVectorAlloc(NUM_INT_EIG_LABELS, MATRIX_REAL) ;
      for (r = 0 ; r < NUM_INT_EIG_LABELS ; r++)
      {
        for (c2 = 1, c = NUM_INT_EIG_LABELS-num_eig ; 
             c < NUM_INT_EIG_LABELS ; 
             c++, c2++)
        {
          *MATRIX_RELT(m_eig, r+1, c2) = intensity_eig_vectors[r][c] ;
        }
      }

      for (r = 1 ; r <= NUM_INT_EIG_LABELS ; r++)
      {
        RVECTOR_ELT(v_obs, r) = 
          label_peaks[intensity_eig_labels[r-1]] - intensity_means[r-1] ;
      }
      v_dot = MatrixMultiply(v_obs, m_eig, NULL) ;
      v_dot_T = VectorTranspose(v_dot, NULL) ;
      v_obs_estimated = MatrixMultiply(m_eig, v_dot_T, NULL) ;
      for (c = 1 ; c <= NUM_INT_EIG_LABELS ; c++)
      {
        float old_peak, new_peak ;
        l = intensity_eig_labels[c-1] ;
        old_peak = label_peaks[l] ;
        new_peak = intensity_means[c-1] + VECTOR_ELT(v_obs_estimated,c) ;
        printf("EIG %s = changing peak from %2.0f to %2.0f, "
               "scale from %2.3f to %2.3f\n",
               cma_label_to_name(l), old_peak, new_peak, 
               label_scales[l], label_scales[l]*new_peak/old_peak) ;
        if (logfp)
          fprintf(logfp, "EIG %s = changing peak from %2.0f to %2.0f, "
                  "scale from %2.3f to %2.3f\n",
                  cma_label_to_name(l), old_peak, new_peak, 
                  label_scales[l], label_scales[l]*new_peak/old_peak) ;
        label_scales[l] *= (new_peak/old_peak) ;
        label_peaks[l] = new_peak ;
      }

      MatrixFree(&m_eig) ;
      VectorFree(&v_dot) ;
      VectorFree(&v_obs) ;
      VectorFree(&v_obs_estimated) ;
      VectorFree(&v_dot_T) ;
    }
#endif

    if (logfp)
    {
      for (l = 0 ; l < MAX_CMA_LABELS ; l++)
        if (computed[l] != 0)
          fprintf(logfp, "label %s: scaling by %2.2f  + %2.1f to %2.0f\n",
                  cma_label_to_name(l),
                  label_scales[l], label_offsets[l], label_peaks[l]) ;
      fflush(logfp) ;
    }
    if (DIAG_VERBOSE_ON)
    {
      FILE *fp ;
      fp = fopen("norm_offset.plt", "w") ;
      for (l = 0 ; l < MAX_CMA_LABELS ; l++)
        if (computed[l] != 0)
          fprintf(fp, "%d %f %f\n", l, label_scales[l], label_offsets[l]) ;
      fclose(fp) ;
    }


    gcaCheck(gca) ;
    for (xn = 0 ; xn < gca->node_width ; xn++)
    {
      double     means_before[MAX_GCA_LABELS], \
      means_after[MAX_GCA_LABELS], scales[MAX_GCA_LABELS];
#if 1
      double     delta_i, delta_j ;
      int        xp, yp, zp ;
#endif
      int        labels[MAX_GCA_LABELS], niter ;
      LABEL_PROB ranks_before[MAX_GCA_LABELS], \
      ranks_after[MAX_GCA_LABELS] ;

      for (yn = 0 ; yn < gca->node_height ; yn++)
      {
        for (zn = 0 ; zn < gca->node_depth ; zn++)
        {
          if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z)
            DiagBreak() ;
          gcan = &gca->nodes[xn][yn][zn] ;
          if (gcan->nlabels <= 0)
            continue ;

          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            gc = &gcan->gcs[i] ;
            l = gcan->labels[i] ;
            labels[i] = l ;
            scales[i] = label_scales[l] ;
            means_before[i] = gc->means[frame] ;
            ranks_before[i].label = l ;
            ranks_before[i].prob = means_before[i] ;
            ranks_before[i].index = i ;
          }
          qsort(ranks_before, gcan->nlabels,
                sizeof(LABEL_PROB), compare_sort_probabilities) ;
          niter = 0 ;
          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            gc = &gcan->gcs[i] ;
            l = gcan->labels[i] ;
            means_after[i] =
              means_before[i]*label_scales[l] + label_offsets[l] ;
            if (means_after[i] < 0)
              means_after[i] = 0 ;
            ranks_after[i].label = l ;
            ranks_after[i].prob = means_after[i] ;
            ranks_after[i].index = i ;
          }
          qsort(ranks_after, gcan->nlabels,
                sizeof(LABEL_PROB), compare_sort_probabilities) ;
          for (i = 0 ; i < gcan->nlabels ; i++)
          {
#if 1
            if (ranks_before[i].label != ranks_after[i].label)
            {
              double    pi, pj, lambda ;
              int       j, ind_j, ind_i ;
              GCA_PRIOR *gcap;

              /* two have swapped position - put them */
              /* back in the right order */
              for (j = 0 ; j < gcan->nlabels ; j++)
                if (ranks_after[j].label == ranks_before[i].label)
                  break ;
              if (j >= gcan->nlabels)
              {
                DiagBreak() ;
                continue ;
              }
              gcaNodeToPrior(gca, xn, yn, zn, &xp, &yp, &zp) ;
              gcap = &gca->priors[xp][yp][zp] ;
              pi = getPrior(gcap, ranks_after[i].label) ;
              pj = getPrior(gcap, ranks_after[j].label) ;
              if (FZERO(pi) && FZERO(pj))
                break ;   // both labels will never happen
              lambda = pi / (pi + pj) ;
              ind_j = ranks_after[j].index ;
              ind_i = ranks_after[i].index ;
              delta_j = (means_after[ind_j] -
                         means_after[ind_i]) * lambda ;
              delta_i = (means_after[ind_i] -
                         means_after[ind_j]) * (1-lambda) ;

              if ((fabs(delta_j) < 1) && (fabs(delta_i) < 1))
              {
                // this will move one mean to the
                // other side of the other
                if ((fabs(delta_j) > fabs(delta_i)) &&
                    !FZERO(delta_j))
                  delta_j /= fabs(delta_j) ;  // make it +-1
                else if (!FZERO(delta_i))
                  delta_i /= fabs(delta_i) ;  // make it +-1
              }
              if (!finite(delta_i) || !finite(delta_j))
              {
                DiagBreak() ;
                break ;
              }
              ranks_after[j].prob =
                means_after[ind_j] = means_after[ind_j] - delta_j ;
              ranks_after[i].prob =
                means_after[ind_i] = means_after[ind_i] - delta_i ;
              if ((xn == Gx && yn == Gy && zn == Gz) &&
                  (ranks_after[i].label == gcan->labels[i] ||
                   ranks_after[j].label == gcan->labels[j] ||
                   Ggca_label < 0))
              {
                printf("ordering of labels %s and %s changed, "
                       "modifying means by %2.0f (%2.1f) "
                       "and %2.0f (%2.1f)\n",
                       cma_label_to_name(ranks_after[i].label),
                       cma_label_to_name(ranks_after[j].label),
                       means_after[i], delta_i,
                       means_after[j], delta_i) ;
              }

              qsort(ranks_after, gcan->nlabels,
                    sizeof(LABEL_PROB),
                    compare_sort_probabilities) ;
              i = -1 ;   /* start loop over */
              if (niter++ > 9)
              {
                DiagBreak() ;
                break ;
              }
              continue ;
            }
#endif
          }

          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            if (FZERO(label_scales[gcan->labels[i]]))
              continue ;
            gc = &gcan->gcs[i] ;
            if ((xn == Gx && yn == Gy && zn == Gz) &&
                (Ggca_label == gcan->labels[i] || Ggca_label < 0))
            {
              printf("scaling gc for label %s at "
                     "(%d, %d, %d) from %2.1f to %2.1f\n",
                     cma_label_to_name(gcan->labels[i]),
                     xn, yn, zn,
                     means_before[i], means_after[i]) ;
              DiagBreak() ;
            }
            gc->means[frame] = means_after[i] ;
            check_finite("after rescaling", gc->means[frame]) ;
          }
        }
      }
    }
    gcaCheck(gca) ;
  }

  if (plta)  // return linear transform array to caller
  {
    int i ;

    // count # of xforms
    for (i = l = 0 ; l < MAX_CMA_LABELS ; l++)
    {
      if (m_by_label[l] != NULL && computed[l] == 2)
        i++ ;
    }

    if (i > 0)  // should always be true
    {
      *plta = lta = LTAalloc(i, mri) ;
      for (i = l = 0 ; l < MAX_CMA_LABELS ; l++)
      {
        if (m_by_label[l] != NULL && computed[l] == 2)
        {
          MatrixCopy(m_by_label[l], lta->xforms[i].m_L) ;
          MatrixFree(&m_by_label[l]) ;
          lta->xforms[i].label = l ;
          i++ ;
        }
      }
    }
    printf("%d transforms computed\n", i) ;
  }

  if (mri_seg)
    MRIfree(&mri_seg) ;
  return(NO_ERROR) ;
}


static float pthresh = 0.5 ;
int
GCAmapRenormalize(GCA *gca, MRI *mri, TRANSFORM *transform)
{
  HISTOGRAM *h, *hsmooth ;
  int       l, xp, yp, zp, nbins, i, x, y, z,
  xn, yn, zn, num, frame, bin ;
  float     fmin, fmax, prior, label_scales[MAX_CMA_LABELS],
  label_modes[MAX_CMA_LABELS],
  modes[MAX_GCA_INPUTS],std, peak, smooth_peak ;
  Real      val/*, scale*/ ;
  GCA_PRIOR *gcap ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  MATRIX    *m_cov ;
  MRI       *mri_fsamples = NULL ;   // diag volume

  /* for each class, build a histogram of values
     (weighted by priors) to determine
     p(I|u,c) p(c).
  */


#if 0
  if (gca->ninputs > 1)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "GCAmapRenormalize: not implemented for ninputs > 1")) ;
#endif

  for (frame = 0 ; frame < mri->nframes ; frame++)
  {
    printf("renormalizing input #%d\n", frame) ;
    MRIvalRangeFrame(mri, &fmin, &fmax, frame) ;
    nbins = 256 ;
    h = HISTOalloc(nbins) ;

    hsmooth = HISTOcopy(h, NULL) ;
    for (l = 0 ; l <= MAX_CMA_LABELS ; l++)  /* don't do Unknown class */
    {
      label_scales[l] = 1 ;  /* mark it as unusable */
      GCAlabelMode(gca, l, modes) ;
      m_cov = GCAlabelCovariance(gca, l, NULL) ;
      if (m_cov == NULL)
        continue ;
      std = 4*sqrt(*MATRIX_RELT(m_cov, frame+1,frame+1)) ;
      MatrixFree(&m_cov) ;
      label_modes[l] = modes[frame] ;
      if (IS_UNKNOWN(l) || IS_INF_LAT_VENT(l))
        continue ;

      printf("%s (%d): mode = %2.2f +- %2.1f\n",
             cma_label_to_name(l), l, label_modes[l], std) ;
      if (l == Gdiag_no)
      {
        mri_fsamples = MRIclone(mri, NULL) ;
        DiagBreak() ;
      }
      if (FZERO(label_modes[l]))
        continue ;
      HISTOclear(h, h) ;
      h->bin_size = (fmax-fmin)/255.0 ;
      if (h->bin_size < 1 && (mri->type == MRI_UCHAR
                              || mri->type == MRI_SHORT))
        h->bin_size = 1 ;
      for (i = 0 ; i < nbins ; i++)
        h->bins[i] = (i+1)*h->bin_size ;

      for (num = xp = 0 ; xp < gca->prior_width ; xp++)
      {
        for (yp = 0 ; yp < gca->prior_height ; yp++)
        {
          for (zp = 0 ; zp < gca->prior_depth ; zp++)
          {
            if (xp == Gxp && yp == Gyp && zp == Gzp)
              DiagBreak() ;
            gcap = &gca->priors[xp][yp][zp] ;
            if (gcap==NULL)
              continue;
            prior = getPrior(gcap, l) ;
            if (prior < pthresh)
              continue ;
            if (!GCApriorToSourceVoxel(gca, mri,
                                       transform,
                                       xp, yp, zp,
                                       &x, &y, &z))
            {
              MRIsampleVolumeFrame(mri, x, y, z, frame, &val) ;
              if (FZERO(val))  // skull stripped
                continue ;
              bin = nint((val - fmin)/h->bin_size) ;
              if (bin >= h->nbins)
                bin = h->nbins-1 ;
              else if (bin < 0)
                bin = 0 ;

              h->counts[bin] += prior ;
              num++ ;
              if (mri_fsamples != NULL)
                MRIsetVoxVal(mri_fsamples, x, y, z, 0, 128) ;
            }
          }
        }
      }
      if (num <= 50)  /* not enough to reliably estimate density */
        continue ;
      HISTOfillHoles(h) ;
      if (l == Gdiag_no)
      {
        HISTOplot(h, "h.plt") ;
        if (mri_fsamples && (Gdiag & DIAG_WRITE))
        {
          char fname[STRLEN] ;
          sprintf(fname, "fsamples%d.mgz", l) ;
          printf("writing fsamples for class %s to %s\n",
                 cma_label_to_name(l), fname) ;
          MRIwrite(mri_fsamples, fname) ;
          MRIfree(&mri_fsamples) ;
        }
        DiagBreak() ;
      }
      HISTOsmooth(h, hsmooth, 1) ;
      if (l == Gdiag_no)
      {
        HISTOplot(hsmooth, "hs.plt") ;
      }
      peak = h->bins[HISTOfindHighestPeakInRegion(h, 0, h->nbins)] ;
      smooth_peak =
        hsmooth->bins[HISTOfindHighestPeakInRegion(hsmooth,
                      0, hsmooth->nbins)] ;

      label_scales[l] = (float)smooth_peak / label_modes[l] ;
      printf("%s (%d): peak at %2.2f, smooth at %2.2f (%d voxels), "
             "scaling by %2.2f\n",
             cma_label_to_name(l), l, peak, smooth_peak, num,
             label_scales[l]) ;
      bin = nint((modes[frame] - fmin)/hsmooth->bin_size) ;
#ifdef WSIZE
#undef WSIZE
#endif
#define WSIZE 11
#define WHALF ((WSIZE-1)/2)
      bin = HISTOfindCurrentPeak(hsmooth, bin, WSIZE, .2) ;
      smooth_peak = hsmooth->bins[bin] ;
      if (bin < 0 || smooth_peak <= 0)
        continue ;
      if (num < 200 && hsmooth->counts[bin] < 5)
        /* not very much data - check more */
      {
        int other_bin ;
        other_bin = HISTOfindPreviousPeak(hsmooth, bin, WHALF) ;
        if (other_bin >= 0)
        {
          if ((hsmooth->counts[other_bin] / hsmooth->counts[bin])
              > 0.9)
          {
            printf("!!!!!!!!!additional peak detected "
                   "at %2.1f (was %2.1f) - unreliable estimate...\n",
                   hsmooth->bins[other_bin], hsmooth->bins[bin]) ;
            label_scales[l] = 1.0 ;
            continue ;
          }
        }
        other_bin = HISTOfindNextPeak(hsmooth, bin, WHALF) ;
        if (other_bin >= 0)
        {
          if (hsmooth->counts[other_bin] / hsmooth->counts[bin] > 0.9)
          {
            printf("!!!!!!!!!additional peak detected "
                   "at %2.1f (was %2.1f) - unreliable estimate...\n",
                   hsmooth->bins[other_bin], hsmooth->bins[bin]) ;
            label_scales[l] = 1.0 ;
            continue ;
          }
        }
      }
      label_scales[l] = (float)smooth_peak / label_modes[l] ;
      if ((label_scales[l] < 0.5 || label_scales[l] > 1.5) &&
          !IS_LAT_VENT(l))
      {
        printf("!!!!!!! rejecting excessive scaling %2.2f\n",
               label_scales[l]) ;
        label_scales[l] = 1.0 ;
      }
      printf("%s (%d): AFTER PRIOR: peak at %2.2f, smooth "
             "at %2.2f (%d voxels), scaling by %2.2f\n",
             cma_label_to_name(l), l, peak, smooth_peak, num,
             label_scales[l]) ;

      if (l == Gdiag_no)
        DiagBreak() ;
      if (l >100)
        break ;
    }

    l = Left_Inf_Lat_Vent ;
    label_scales[Left_Inf_Lat_Vent] =
      (.25+.75*label_scales[Left_Lateral_Ventricle]);
    printf("%s (%d): scaling by %2.2f = %2.1f "
           "(based on %2.2f for lateral ventricle)\n",
           cma_label_to_name(l), l, label_scales[Left_Inf_Lat_Vent],
           label_modes[Left_Inf_Lat_Vent]*label_scales[Left_Inf_Lat_Vent],
           label_scales[Left_Lateral_Ventricle]) ;
    l = Right_Inf_Lat_Vent ;
    label_scales[Right_Inf_Lat_Vent] =
      (.25+.75*label_scales[Right_Lateral_Ventricle]) ;
    printf("%s (%d): scaling by %2.2f = %2.1f "
           "(based on %2.2f for lateral ventricle)\n",
           cma_label_to_name(l), l, label_scales[Right_Inf_Lat_Vent],
           label_modes[Right_Inf_Lat_Vent]*label_scales[Right_Inf_Lat_Vent],
           label_scales[Right_Lateral_Ventricle]) ;


    for (xn = 0 ; xn < gca->node_width ; xn++)
    {
      double     means_before[MAX_GCA_LABELS], \
      means_after[MAX_GCA_LABELS], scales[MAX_GCA_LABELS] ;
      int        labels[MAX_GCA_LABELS], niter ;
      LABEL_PROB ranks_before[MAX_GCA_LABELS], \
      ranks_after[MAX_GCA_LABELS] ;

      for (yn = 0 ; yn < gca->node_height ; yn++)
      {
        for (zn = 0 ; zn < gca->node_depth ; zn++)
        {
          if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z)
            DiagBreak() ;
          gcan = &gca->nodes[xn][yn][zn] ;
          if (gcan->nlabels <= 0)
            continue ;

          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            gc = &gcan->gcs[i] ;
            l = gcan->labels[i] ;
            labels[i] = l ;
            scales[i] = label_scales[l] ;
            means_before[i] = gc->means[frame] ;
            ranks_before[i].label = l ;
            ranks_before[i].prob = means_before[i] ;
            ranks_before[i].index = i ;
          }
          qsort(ranks_before, gcan->nlabels,
                sizeof(LABEL_PROB), compare_sort_probabilities) ;
          niter = 0 ;
          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            gc = &gcan->gcs[i] ;
            l = gcan->labels[i] ;
            means_after[i] = means_before[i]*scales[i] ;
            ranks_after[i].label = l ;
            ranks_after[i].prob = means_after[i] ;
            ranks_after[i].index = i ;
          }
          qsort(ranks_after, gcan->nlabels,
                sizeof(LABEL_PROB), compare_sort_probabilities) ;
          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            if (ranks_before[i].label != ranks_after[i].label)
            {
#if 1
              double    pi, pj, lambda, delta_i, delta_j ;
              int       j, ind_j, ind_i ;
              GCA_PRIOR *gcap;

              /* two have swapped position - put them */
              /* back in the right order */
              for (j = 0 ; j < gcan->nlabels ; j++)
                if (ranks_after[j].label == ranks_before[i].label)
                  break ;
              if (j >= gcan->nlabels)
              {
                DiagBreak() ;
                continue ;
              }
              gcaNodeToPrior(gca, xn, yn, zn, &xp, &yp, &zp) ;
              gcap = &gca->priors[xp][yp][zp] ;
              pi = getPrior(gcap, ranks_after[i].label) ;
              pj = getPrior(gcap, ranks_after[j].label) ;
              if (FZERO(pi) && FZERO(pj))
                break ;   // both labels will never happen
              lambda = pi / (pi + pj) ;
              ind_j = ranks_after[j].index ;
              ind_i = ranks_after[i].index ;
              delta_j =
                (means_after[ind_j] - means_after[ind_i]) *
                lambda ;
              delta_i = (means_after[ind_i] - means_after[ind_j]) *
                        (1-lambda) ;

              if ((fabs(delta_j) < 1) && (fabs(delta_i) < 1))
              {
                // this will move one mean to the
                // other side of the other
                if ((fabs(delta_j) > fabs(delta_i)) &&
                    !FZERO(delta_j))
                  delta_j /= fabs(delta_j) ;  // make it +-1
                else if (!FZERO(delta_i))
                  delta_i /= fabs(delta_i) ;  // make it +-1
              }
              if (!finite(delta_i) || !finite(delta_j))
              {
                DiagBreak() ;
                break ;
              }
              ranks_after[j].prob =
                means_after[ind_j] = means_after[ind_j] - delta_j ;
              ranks_after[i].prob =
                means_after[ind_i] = means_after[ind_i] - delta_i ;
              if ((xn == Gx && yn == Gy && zn == Gz) &&
                  (ranks_after[i].label == gcan->labels[i] ||
                   ranks_after[j].label == gcan->labels[j] ||
                   Ggca_label < 0))
              {
                printf("ordering of labels %s and %s changed, "
                       "modifying means by %2.0f (%2.1f) "
                       "and %2.0f (%2.1f)\n",
                       cma_label_to_name(ranks_after[i].label),
                       cma_label_to_name(ranks_after[j].label),
                       means_after[i], delta_i,
                       means_after[j], delta_i) ;
              }

#else
              double diff, avg ;
              int    j ;

              /* two have swapped position - put them */
              /* back in the right order */
              for (j = 0 ; j < gcan->nlabels ; j++)
                if (ranks_after[j].label == ranks_before[i].label)
                  break ;
              diff = means_before[ranks_after[i].index] -
                     means_before[ranks_before[i].index];
              avg = (means_after[ranks_after[i].index] +
                     means_after[ranks_before[i].index]) / 2 ;
              ranks_after[i].prob =
                means_after[ranks_after[i].index] =
                  avg+diff/4 ;
              ranks_after[j].prob =
                means_after[ranks_after[j].index] =
                  avg-diff/4 ;
#endif
              qsort(ranks_after, gcan->nlabels,
                    sizeof(LABEL_PROB),
                    compare_sort_probabilities) ;
              i = -1 ;   /* start loop over */
              if (niter++ > 9)
              {
                DiagBreak() ;
                break ;
              }
              continue ;
            }
          }

          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            if (FZERO(label_scales[gcan->labels[i]]))
              continue ;
            gc = &gcan->gcs[i] ;
            if ((xn == Ggca_x && yn == Ggca_y && zn == Ggca_z) &&
                (Ggca_label == gcan->labels[i] || Ggca_label < 0))
            {
              printf("scaling gc for label %s at "
                     "(%d, %d, %d) from %2.1f to %2.1f\n",
                     cma_label_to_name(gcan->labels[i]),
                     xn, yn, zn,
                     means_before[i], means_after[i]) ;
              DiagBreak() ;
            }
            gc->means[frame] = means_after[i] ;
          }
        }
      }
    }
  }

  return(NO_ERROR) ;
}



int
GCAmapRenormalizeByClass(GCA *gca, MRI *mri, TRANSFORM *transform)
{
  HISTOGRAM *h, *hsmooth ;
  int       l, nbins, i, x, y, z, max_p_label,
  xn, yn, zn, num, frame, bin, n, label, c, max_label ;
  float     fmin, fmax, prior, label_scales[MAX_CMA_LABELS],
  class_modes[NTISSUE_CLASSES], class_scales[NTISSUE_CLASSES],
  modes[MAX_GCA_INPUTS], peak, smooth_peak ;
  Real      val ;
  float     vals[MAX_GCA_INPUTS] ;
  GCA_PRIOR *gcap ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  double    p, max_p ;

  /* for each class, build a histogram of values
     (weighted by priors) to determine
     p(I|u,c) p(c).
  */

  max_label = GCAmaxLabel(gca) ;

#if 0
  if (gca->ninputs > 1)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "GCAmapRenormalize: not implemented for ninputs > 1")) ;
#endif

  for (frame = 0 ; frame < mri->nframes ; frame++)
  {
    printf("renormalizing input #%d\n", frame) ;
    MRIvalRangeFrame(mri, &fmin, &fmax, frame) ;
    nbins = 256 ;
    h = HISTOalloc(nbins) ;

    hsmooth = HISTOcopy(h, NULL) ;
    for (c = 0 ;  c < NTISSUE_CLASSES ; c++)  /* don't do Unknown class */
    {
      class_scales[c] = 1 ;  /* mark it as unusable */
      GCAclassMode(gca, c, modes) ;
      class_modes[c] = modes[frame] ;
      printf("%s (%d): mode = %2.2f\n",
             c == CSF_CLASS ? "CSF" : c == GM_CLASS ? "GM" : "WM",
             c, class_modes[c]) ;
      if (c == Gdiag_no)
        DiagBreak() ;
      if (FZERO(class_modes[c]))
        continue ;
      HISTOclear(h, h) ;
      h->bin_size = (fmax-fmin)/255.0 ;
      if (h->bin_size < 1 && (mri->type == MRI_UCHAR
                              || mri->type == MRI_SHORT))
        h->bin_size = 1 ;
      for (i = 0 ; i < nbins ; i++)
        h->bins[i] = (i+1)*h->bin_size ;

      for (num = x = 0 ; x < mri->width ; x++)
      {
        for (y = 0 ; y < mri->height ; y++)
        {
          for (z = 0 ; z < mri->depth ; z++)
          {
            if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
              DiagBreak() ;
            if (GCAsourceVoxelToNode(gca, mri,
                                     transform, x, y, z,
                                     &xn, &yn, &zn) != NO_ERROR)
              continue ;
            gcan = &gca->nodes[xn][yn][zn] ;
            gcap = getGCAP(gca, mri, transform, x, y, z) ;
            if (gcap==NULL || gcan == NULL)
              continue;
            if (gcan->nlabels == 1 && IS_UNKNOWN(gcan->labels[0]))
              continue ;
            load_vals(mri, x, y, z, vals, gca->ninputs);
            val = vals[frame] ;
            if (FZERO(val))  // skull stripped
              continue ;


            // find class with highest posterior likilihood
            max_p = GCAcomputePosteriorDensity
                    (gcap, gcan, 0, vals, gca->ninputs) ;
            max_p_label = gcan->labels[0] ;

            for (n = 1 ; n < gcan->nlabels ; n++)
            {
              label = gcan->labels[n] ;

              p = GCAcomputePosteriorDensity(gcap, gcan, n, vals,
                                             gca->ninputs) ;
              if (p > max_p)
              {
                max_p = p ;
                max_p_label = gcan->labels[n] ;
              }
            }

            if (IS_CLASS(max_p_label,c) == 0)
              // a label in a different class
              continue ;
            prior = getPrior(gcap, max_p_label) ;
            if (prior < pthresh)
              continue ;

            bin = nint((val - fmin)/h->bin_size) ;
            if (bin >= h->nbins)
              bin = h->nbins-1 ;
            else if (bin < 0)
              bin = 0 ;

            h->counts[bin] += prior ;
            num++ ;
          }
        }
      }
      if (num <= 50)  /* not enough to reliably estimate density */
        continue ;
      HISTOfillHoles(h) ;
      if (c == Gdiag_no)
      {
        HISTOplot(h, "h.plt") ;
        DiagBreak() ;
      }
      HISTOsmooth(h, hsmooth, 1) ;
      if (c == Gdiag_no)
      {
        HISTOplot(hsmooth, "hs.plt") ;
      }
      peak = h->bins[HISTOfindHighestPeakInRegion(h, 0, h->nbins)] ;
      smooth_peak =
        hsmooth->bins[HISTOfindHighestPeakInRegion(hsmooth,
                      0, hsmooth->nbins)] ;

      class_scales[c] = (float)smooth_peak / class_modes[c] ;
      printf("%s (%d): peak at %2.2f, smooth at %2.2f (%d voxels), "
             "scaling by %2.2f\n",
             c == CSF_CLASS ? "CSF" : c == GM_CLASS ? "GM" : "WM",
             c, peak, smooth_peak, num, class_scales[c]) ;
#if 0
      bin = nint((modes[frame] - fmin)/hsmooth->bin_size) ;
#ifdef WSIZE
#undef WSIZE
#endif
#define WSIZE 11
#define WHALF ((WSIZE-1)/2)
      bin = HISTOfindCurrentPeak(hsmooth, bin, WSIZE, .2) ;
      smooth_peak = hsmooth->bins[bin] ;
      if (bin < 0 || smooth_peak <= 0)
        continue ;
      if (num < 200 && hsmooth->counts[bin] < 5)
        /* not very much data - check more */
      {
        int other_bin ;
        other_bin = HISTOfindPreviousPeak(hsmooth, bin, WHALF) ;
        if (other_bin >= 0)
        {
          if ((hsmooth->counts[other_bin] / hsmooth->counts[bin])
              > 0.9)
          {
            printf("!!!!!!!!!additional peak detected "
                   "at %2.1f (was %2.1f) - unreliable estimate...\n",
                   hsmooth->bins[other_bin], hsmooth->bins[bin]) ;
            class_scales[c] = 1.0 ;
            continue ;
          }
        }
        other_bin = HISTOfindNextPeak(hsmooth, bin, WHALF) ;
        if (other_bin >= 0)
        {
          if (hsmooth->counts[other_bin] / hsmooth->counts[bin] > 0.9)
          {
            printf("!!!!!!!!!additional peak detected "
                   "at %2.1f (was %2.1f) - unreliable estimate...\n",
                   hsmooth->bins[other_bin], hsmooth->bins[bin]) ;
            class_scales[c] = 1.0 ;
            continue ;
          }
        }
      }
      class_scales[c] = (float)smooth_peak / class_modes[c] ;
      printf("%s (%d): AFTER PRIOR: peak at %2.2f, smooth "
             "at %2.2f (%d voxels), scaling by %2.2f\n",
             c == CSF_CLASS ? "CSF" : c == GM_CLASS ? "GM" : "WM",
             c, peak, smooth_peak, num,
             class_scales[c]) ;
#endif
      if (c == Gdiag_no)
        DiagBreak() ;
    }

    for (l = 0 ; l <= max_label ; l++)
    {
      GCAlabelMode(gca, l, modes) ;
      if (FZERO(modes[frame]))  // no real data
      {
        label_scales[l] = 1.0 ;
        continue ;
      }

      // computed from manually labeled images
      //(gray matter coef, wm is 1-l)
#define L_THALAMUS  0.6
#define L_PALLIDUM  0.6
#define L_PUTAMEN   0.8
#define L_VENTRALDC 0.2      // mostly white

      if (IS_GRAY_CLASS(l))
        label_scales[l] = class_scales[GM_CLASS] ;
      else if (IS_CSF_CLASS(l))
        label_scales[l] = class_scales[CSF_CLASS] ;
      else if (IS_WHITE_CLASS(l))
        label_scales[l] = class_scales[WM_CLASS] ;
      else
        switch (l)
        {
        case Right_VentralDC:
        case Left_VentralDC:
          label_scales[l] =
            L_VENTRALDC*class_scales[GM_CLASS] +
            (1-L_VENTRALDC)*class_scales[WM_CLASS];
          break ;
        case Left_Thalamus:
        case Right_Thalamus:
        case Left_Thalamus_Proper:
        case Right_Thalamus_Proper:
          //      GCAlabelMean(gca, Left_Thalamus, means) ;
          //      GCAlabelMean(gca, Right_Thalamus, rmeans) ;
          //      means[frame] = (means[frame] + rmeans[frame]) / 2 ;
          label_scales[l] =
            L_THALAMUS*class_scales[GM_CLASS] +
            (1-L_THALAMUS)*class_scales[WM_CLASS];
          break ;
        case Left_Pallidum:
        case Right_Pallidum:
          label_scales[l] =
            L_PALLIDUM*class_scales[GM_CLASS] +
            (1-L_PALLIDUM)*class_scales[WM_CLASS];
          break ;
        case Left_Putamen:
        case Right_Putamen:
          label_scales[l] =
            (L_PUTAMEN)*class_scales[GM_CLASS] +
            (1-L_PUTAMEN)*class_scales[WM_CLASS];
          break ;
        case Left_Inf_Lat_Vent:  // just CSF
        case Right_Inf_Lat_Vent:
          label_scales[l] = class_scales[CSF_CLASS] ;
          break ;
        case Left_Hippocampus:   // just GM
        case Right_Hippocampus:
        case Left_Amygdala:
        case Right_Amygdala:
        case Left_Accumbens_area:
        case Right_Accumbens_area:
        case Left_Cerebellum_Cortex:
        case Left_Cerebellum_Exterior:
        case Right_Cerebellum_Cortex:
        case Right_Cerebellum_Exterior:
        case Right_Cerebral_Exterior:
        case Left_Cerebral_Exterior:
          label_scales[l] = class_scales[GM_CLASS] ;
          break ;
        case Right_Cerebellum_White_Matter:  // just WM
        case Left_Cerebellum_White_Matter:
        case Brain_Stem:
          label_scales[l] = class_scales[WM_CLASS] ;
          break ;
        default:
          label_scales[l] = 1.0 ;
        }
      printf("%s (%d): scaling by %2.2f = %2.1f (was %2.1f)\n",
             cma_label_to_name(l), l,
             label_scales[l], modes[frame]*label_scales[l], modes[frame]) ;

    }

    for (xn = 0 ; xn < gca->node_width ; xn++)
    {
      double     means_before[MAX_GCA_LABELS], \
      means_after[MAX_GCA_LABELS], scales[MAX_GCA_LABELS] ;
      int        labels[MAX_GCA_LABELS], niter ;
      LABEL_PROB ranks_before[MAX_GCA_LABELS], \
      ranks_after[MAX_GCA_LABELS] ;

      for (yn = 0 ; yn < gca->node_height ; yn++)
      {
        for (zn = 0 ; zn < gca->node_depth ; zn++)
        {
          if (xn == Ggca_x && yn == Ggca_y && zn == Ggca_z)
            DiagBreak() ;
          gcan = &gca->nodes[xn][yn][zn] ;
          if (gcan->nlabels <= 0)
            continue ;

          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            gc = &gcan->gcs[i] ;
            l = gcan->labels[i] ;
            labels[i] = l ;
            scales[i] = label_scales[l] ;
            means_before[i] = gc->means[frame] ;
            ranks_before[i].label = l ;
            ranks_before[i].prob = means_before[i] ;
            ranks_before[i].index = i ;
          }
          qsort(ranks_before, gcan->nlabels,
                sizeof(LABEL_PROB), compare_sort_probabilities) ;
          niter = 0 ;
          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            gc = &gcan->gcs[i] ;
            l = gcan->labels[i] ;
            means_after[i] = means_before[i]*scales[i] ;
            ranks_after[i].label = l ;
            ranks_after[i].prob = means_after[i] ;
            ranks_after[i].index = i ;
          }
          qsort(ranks_after, gcan->nlabels,
                sizeof(LABEL_PROB), compare_sort_probabilities) ;
          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            if (ranks_before[i].label != ranks_after[i].label)
            {
              double diff, avg ;
              int    j ;

              /* two have swapped position - put them */
              /* back in the right order */
              for (j = 0 ; j < gcan->nlabels ; j++)
                if (ranks_after[j].label == ranks_before[i].label)
                  break ;
              diff = means_before[ranks_after[i].index] -
                     means_before[ranks_before[i].index];
              avg = (means_after[ranks_after[i].index] +
                     means_after[ranks_before[i].index]) / 2 ;
              ranks_after[i].prob =
                means_after[ranks_after[i].index] =
                  avg+diff/4 ;
              ranks_after[j].prob =
                means_after[ranks_after[j].index] =
                  avg-diff/4 ;
              qsort(ranks_after, gcan->nlabels,
                    sizeof(LABEL_PROB),
                    compare_sort_probabilities) ;
              i = -1 ;   /* start loop over */
              if (niter++ > 9)
              {
                DiagBreak() ;
                break ;
              }
              continue ;
            }
          }

          for (i = 0 ; i < gcan->nlabels ; i++)
          {
            if (FZERO(label_scales[gcan->labels[i]]))
              continue ;
            gc = &gcan->gcs[i] ;
            if ((xn == Ggca_x && yn == Ggca_y && zn == Ggca_z) &&
                (Ggca_label == gcan->labels[i] || Ggca_label < 0))
            {
              printf("scaling gc for label %s at "
                     "(%d, %d, %d) from %2.1f to %2.1f\n",
                     cma_label_to_name(gcan->labels[i]),
                     xn, yn, zn,
                     means_before[i], means_after[i]) ;
              DiagBreak() ;
            }
            gc->means[frame] = means_after[i] ;
          }
        }
      }
    }
  }

  return(NO_ERROR) ;
}



#define NLABELS 4

static int labels[NLABELS] =
  {
    Dura, Bone, SC_FAT_MUSCLE, CSF_SA
  } ;
MRI *
GCArelabelNonbrain(GCA *gca,
                   MRI *mri_inputs,
                   MRI *mri_src,
                   MRI *mri_dst,
                   TRANSFORM *transform)
{
  int       x, y, z, xn, yn, zn, width, height, depth, label,
  i, total_changed = 0, n, nchanged ;
  int      max_i = 0;
  GC1D      *gcs[NLABELS] ;
  double    pvals[NLABELS], max_p ;
  GCA_NODE  *gcan ;
  float     vals[MAX_GCA_INPUTS],
  means[NLABELS][MAX_GCA_INPUTS],
  vars[NLABELS][MAX_GCA_INPUTS], dist ;
  MRI       *mri_tmp ;

  if (mri_src != mri_dst)
    mri_dst = MRIcopy(mri_src, mri_dst) ;

  mri_tmp = MRIcopy(mri_dst, NULL) ;

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

  for (i = 0 ; i < NLABELS ; i++)
    GCAcomputeLabelStats(gca, labels[i], vars[i], means[i]) ;

  /* replace Epidermis with SC_FAT */
  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        label = nint(MRIgetVoxVal(mri_src, x, y, z, 0)) ;
        if (label == Epidermis)
          label = SC_FAT_MUSCLE ;
        if (label == Cranium)
          label = Bone ;
        MRIsetVoxVal(mri_src, x, y, z, 0, label) ;
        MRIsetVoxVal(mri_tmp, x, y, z, 0, label) ;
      }
    }
  }

  do
  {
    nchanged = 0 ;
    for (x = 0 ; x < width ; x++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (z = 0 ; z < depth ; z++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;

          label = nint(MRIgetVoxVal(mri_tmp, x, y, z, 0)) ;

          if (label == SC_FAT_MUSCLE)
            /* check to see whether at borders of skull */
          {
            if (MRIneighborsInWindow(mri_src, x, y, z, 3, Unknown)
                > 1)
              continue ;
          }

          if (label != Dura &&
              label != Bone &&
              label != SC_FAT_MUSCLE &&
              label != CSF_SA)
            continue ;
          if (MRIneighborsInWindow(mri_src, x, y, z, 3, label) >= 24)
            continue ;   /* in the body of the label - ignore */
          if (MRIneighborsInWindow(mri_src, x, y, z, 5, label) >= 100)
            continue ;   /* in the body of the label - ignore */
          load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
          if (!GCAsourceVoxelToNode(gca, mri_inputs, transform,
                                    x, y, z, &xn, &yn, &zn))
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            max_i = -1 ;
            max_p = -1 ;
            for (i = 0 ; i < NLABELS ; i++)
            {
              gcs[i] = GCAfindGC(gca, xn, yn, zn, labels[i]) ;
              if (gcs[i] == NULL)
                gcs[i]
                = findGCInWindow(gca, xn, yn, zn, labels[i], 3) ;
              if (gcs[i] == NULL)
              {
                DiagBreak() ;
                continue ;
              }
              for (dist = 0, n = 0 ; n < gca->ninputs ; n++)
                dist += SQR(vals[n]-means[i][n]) ;
              pvals[i] = exp(-dist) ;
              if (pvals[i] >= max_p)
              {
                max_p = pvals[i] ;
                max_i = i ;
              }
            }
          }
          ///////////////
          if ((labels[max_i] == Dura) &&
              (MRIneighborsInWindow(mri_tmp, x, y, z, 5, Bone)+
               MRIneighborsInWindow(mri_tmp, x, y, z, 5,
                                    SC_FAT_MUSCLE)>=120))
            continue ;
          if (labels[max_i] != label && ((x == Ggca_x && y ==
                                          Ggca_y && z == Ggca_z)))
          {
            printf("GCArelabelNonbrain: changing label at "
                   "(%d, %d, %d) from %s (%d) to %s (%d)\n",
                   x, y, z, cma_label_to_name(label), label,
                   cma_label_to_name(labels[max_i]), labels[max_i]) ;
          }
          MRIsetVoxVal(mri_tmp, x, y, z, 0, labels[max_i]) ;
          if (labels[max_i] != label)
            nchanged++ ;
        }
      }
    }
    total_changed += nchanged ;
    break ;
  }
  while (nchanged > 0) ;

  /* look for dura between bone and skin -
     it is partial volumed of these two */
  do
  {
    nchanged = 0 ;
    for (x = 0 ; x < width ; x++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (z = 0 ; z < depth ; z++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;

          label = MRIgetVoxVal(mri_tmp, x, y, z, 0) ;

          if (label != Dura)
            continue ;
          if (((MRIneighborsInWindow(mri_tmp, x, y, z, 3, Bone) < 7) ||
               (MRIneighborsInWindow(mri_tmp, x, y, z, 3,
                                     SC_FAT_MUSCLE) < 7)) &&
              ((MRIneighborsInWindow(mri_tmp, x, y, z, 5,
                                     Bone) < 30) ||
               (MRIneighborsInWindow(mri_tmp, x, y, z, 5,
                                     SC_FAT_MUSCLE) < 30)))
            continue ;
          load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
          if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                    transform, x, y, z, &xn, &yn, &zn))
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            max_i = -1 ;
            max_p = -1 ;
            for (i = 0 ; i < NLABELS ; i++)
            {
              if (labels[i] != SC_FAT_MUSCLE && labels[i] != Bone)
                continue ;
              gcs[i] = GCAfindGC(gca, xn, yn, zn, labels[i]) ;
              if (gcs[i] == NULL)
                gcs[i] = findGCInWindow(gca, xn, yn, zn,
                                        labels[i], 3) ;
              if (gcs[i] == NULL)
              {
                DiagBreak() ;
                continue ;
              }
              for (dist = 0, n = 0 ; n < gca->ninputs ; n++)
                dist += SQR(vals[n]-means[i][n]) ;
              pvals[i] = exp(-dist) ;
              if (pvals[i] >= max_p)
              {
                max_p = pvals[i] ;
                max_i = i ;
              }
            }
          }
          /////////////////////////
          if (labels[max_i] != label &&
              ((x == Ggca_x && y == Ggca_y && z == Ggca_z)))
          {
            printf("GCArelabelNonbrain: changing label at "
                   "(%d, %d, %d) from %s (%d) to %s (%d)\n",
                   x, y, z, cma_label_to_name(label), label,
                   cma_label_to_name(labels[max_i]), labels[max_i]) ;
          }
          MRIsetVoxVal(mri_tmp, x, y, z, 0, labels[max_i]) ;
          if (labels[max_i] != label)
            nchanged++ ;
        }
      }
    }
    total_changed += nchanged ;
    printf("%d voxels labeled dura changed to bone or skin...\n", nchanged) ;
    if (nchanged < 10)
      break ;
  }
  while (nchanged > 0) ;

  /* change dura and cortex labels that are
     in the middle of tons of SC_FAT to SC_FAT */
  do
  {
    nchanged = 0 ;
    for (x = 0 ; x < width ; x++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (z = 0 ; z < depth ; z++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;

          label = nint(MRIgetVoxVal(mri_tmp, x, y, z, 0)) ;

          if (label != Dura && !IS_CORTEX(label))
            continue ;
          if (MRIneighborsInWindow(mri_tmp, x, y, z,
                                   5, SC_FAT_MUSCLE) < 100)
            continue ;
          label = SC_FAT_MUSCLE ;
          if ((label != nint(MRIgetVoxVal(mri_tmp, x, y, z, 0))) &&
              ((x == Ggca_x && y == Ggca_y && z == Ggca_z)))
          {
            printf("GCArelabelNonbrain: changing label at "
                   "(%d, %d, %d) from %s (%d) to %s (%d)\n",
                   x, y, z,
                   cma_label_to_name(nint(MRIgetVoxVal(mri_tmp,x,y,z,0))),
                   nint(MRIgetVoxVal(mri_tmp,x,y,z,0)),
                   cma_label_to_name(label), label) ;
          }
          if (label != nint(MRIgetVoxVal(mri_tmp, x, y, z, 0)))
            nchanged++ ;
          MRIsetVoxVal(mri_tmp, x, y, z, 0, label) ;
        }
      }
    }
    total_changed += nchanged ;
    printf("%d voxels labeled dura and/or cortex changed to skin...\n",
           nchanged) ;
    if (nchanged < 10)
      break ;
  }
  while (nchanged > 0) ;

  MRIcopy(mri_tmp, mri_dst) ;
  MRIfree(&mri_tmp) ;

  printf("%d voxels changed in MRIrelabelNonbrain\n", total_changed) ;
  return(mri_dst) ;
}



int
GCAreplaceLabels(GCA *gca, int in_label, int out_label)
{
  int       x, y, z, n ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
          if (gcan->labels[n] == in_label)
            gcan->labels[n] = out_label ;
      }
    }
  }

  for (x = 0 ; x < gca->prior_width ; x++)
  {
    for (y = 0 ; y < gca->prior_height ; y++)
    {
      for (z = 0 ; z < gca->prior_depth ; z++)
      {
        gcap = &gca->priors[x][y][z] ;
        if (gcap==NULL)
          continue;
        for (n = 0 ; n < gcap->nlabels ; n++)
          if (gcap->labels[n] == in_label)
            gcap->labels[n] = out_label ;
      }
    }
  }

  return(NO_ERROR) ;
}


int
GCAreplaceRightWithLeft(GCA *gca)
{
  GCAreplaceLabels(gca, Right_Cerebral_Exterior, Left_Cerebral_Exterior) ;
  GCAreplaceLabels(gca, Right_Cerebral_White_Matter,
                   Left_Cerebral_White_Matter) ;
  GCAreplaceLabels(gca, Right_Cerebral_Cortex, Left_Cerebral_Cortex) ;
  GCAreplaceLabels(gca, Right_Lateral_Ventricle, Left_Lateral_Ventricle) ;
  GCAreplaceLabels(gca, Right_Inf_Lat_Vent, Left_Inf_Lat_Vent) ;
  GCAreplaceLabels(gca, Right_Cerebellum_Exterior, Left_Cerebellum_Exterior) ;
  GCAreplaceLabels(gca, Right_Cerebellum_White_Matter,
                   Left_Cerebellum_White_Matter) ;
  GCAreplaceLabels(gca, Right_Cerebellum_Cortex, Left_Cerebellum_Cortex) ;
  GCAreplaceLabels(gca, Right_Thalamus, Left_Thalamus) ;
  GCAreplaceLabels(gca, Right_Thalamus_Proper, Left_Thalamus_Proper) ;
  GCAreplaceLabels(gca, Right_Caudate, Left_Caudate) ;
  GCAreplaceLabels(gca, Right_Putamen, Left_Putamen) ;
  GCAreplaceLabels(gca, Right_Pallidum, Left_Pallidum) ;
  GCAreplaceLabels(gca, Right_Hippocampus, Left_Hippocampus) ;
  GCAreplaceLabels(gca, Right_Amygdala, Left_Amygdala) ;
  GCAreplaceLabels(gca, Right_Insula, Left_Insula) ;
  GCAreplaceLabels(gca, Right_Operculum, Left_Operculum) ;
  GCAreplaceLabels(gca, Right_Lesion, Left_Lesion) ;
  GCAreplaceLabels(gca, Right_Accumbens_area, Left_Accumbens_area) ;
  GCAreplaceLabels(gca, Right_Substancia_Nigra, Left_Substancia_Nigra) ;
  GCAreplaceLabels(gca, Right_VentralDC, Left_VentralDC) ;
  GCAreplaceLabels(gca, Right_undetermined, Left_undetermined) ;
  return(NO_ERROR) ;
}



GCA_NODE *
GCAbuildRegionalGCAN(GCA *gca, int xn, int yn, int zn, int wsize)
{
  GCA_NODE *gcan, *gcan_nbr ;
  GCA_PRIOR *gcap ;
  int      n, xi, yi, zi, xk, yk, zk, nlabels = 0, whalf,
      xp, yp, zp, label, total_training[MAX_CMA_LABELS+1],
      used[MAX_CMA_LABELS+1] ;
  float    label_priors[MAX_CMA_LABEL+1], p ;
  MATRIX   *m_cov[MAX_CMA_LABEL+1], *m_tmp = NULL ;
  VECTOR   *v_means[MAX_CMA_LABEL+1], *v_tmp = NULL ;

  gcan = calloc(1, sizeof(GCA_NODE)) ;

  memset(label_priors, 0, sizeof(label_priors)) ;
  memset(total_training, 0, sizeof(total_training)) ;
  memset(used, 0, sizeof(used)) ;
  nlabels = 0 ;
  whalf = (wsize-1)/2 ;
  for (xk = -whalf ; xk <= whalf ; xk++)
  {
    xi = xn + xk ;
    if (xi < 0 || xi >= gca->node_width)
      continue ;
    for (yk = -whalf ; yk <= whalf ; yk++)
    {
      yi = yn + yk ;
      if (yi < 0 || yi >= gca->node_height)
        continue ;
      for (zk = -whalf ; zk <= whalf ; zk++)
      {
        zi = zn + zk ;
        if (zi < 0 || zi >= gca->node_depth)
          continue ;
        gcan_nbr = &gca->nodes[xi][yi][zi] ;
        if (gcaNodeToPrior(gca, xi, yi, zi, &xp, &yp, &zp) != NO_ERROR)
          continue ;
        gcap = &gca->priors[xp][yp][zp] ;
        for (n = 0 ; n < gcan_nbr->nlabels ; n++)
        {
          label = gcan_nbr->labels[n] ;
          if (used[label] == 0)  /* first time for this label */
          {
            used[label] = 1 ;
            m_cov[label] =
              load_covariance_matrix(&gcan_nbr->gcs[n],
                                     NULL, gca->ninputs) ;
            v_means[label] =
              load_mean_vector(&gcan_nbr->gcs[n],
                               NULL, gca->ninputs) ;
            MatrixClear(m_cov[label]) ;
            MatrixClear(v_means[label]) ;
            nlabels++ ;
          }
          total_training[label] += gcan_nbr->gcs[n].ntraining ;
          p = getPrior(gcap, label) ;
          label_priors[label] += p ;

          m_tmp =
            load_covariance_matrix(&gcan_nbr->gcs[n],
                                   m_tmp, gca->ninputs) ;
          MatrixScalarMul(m_tmp, p, m_tmp) ;
          MatrixAdd(m_tmp, m_cov[label], m_cov[label]) ;

          v_tmp = load_mean_vector(&gcan_nbr->gcs[n],
                                   v_tmp, gca->ninputs) ;
          MatrixScalarMul(v_tmp, p, v_tmp) ;
          MatrixAdd(v_tmp, v_means[label], v_means[label]) ;
        }
        gcan->total_training += gcan_nbr->total_training ;
      }
    }
  }

  gcan->nlabels = gcan->max_labels = nlabels ;
  gcan->gcs = alloc_gcs(nlabels, GCA_NO_MRF, gca->ninputs) ;
  gcan->labels = (unsigned short *)calloc(nlabels, sizeof(unsigned short)) ;

  for (nlabels = 0, n = 0 ; n <= MAX_CMA_LABELS ; n++)
  {
    if (used[n] > 0)
    {
      gcan->labels[nlabels] = n ;
      MatrixScalarMul(m_cov[n], 1.0/label_priors[n], m_cov[n]) ;
      MatrixScalarMul(v_means[n], 1.0/label_priors[n], v_means[n]) ;
      set_mean_vector(&gcan->gcs[nlabels], v_means[n], gca->ninputs) ;
      set_covariance_matrix(&gcan->gcs[nlabels], m_cov[n], gca->ninputs) ;
      MatrixFree(&m_cov[n]) ;
      VectorFree(&v_means[n]) ;
      gcan->gcs[nlabels].ntraining = total_training[n] ;
      nlabels++ ;
    }
  }

  MatrixFree(&m_tmp) ;
  VectorFree(&v_tmp) ;
  return(gcan) ;
}


int
GCAfreeRegionalGCAN(GCA_NODE **pgcan)
{
  GCA_NODE *gcan ;

  gcan = *pgcan ;
  *pgcan = NULL ;
  free_gcs(gcan->gcs, GCA_NO_MRF, gcan->nlabels) ;
  free(gcan->labels) ;
  free(gcan) ;
  return(NO_ERROR) ;
}



GCA *GCAcompactify(GCA *gca)
{
  int width, height, depth;
  GCA_PRIOR *gcap = 0;
  GCA_NODE  *gcan = 0;
  float *old_priors;
  unsigned short *old_labels;
  GC1D *old_gcs;
  int n, nmax;
  int i,j, k;
  double byteSaved = 0.;

  width = gca->prior_width;
  height = gca->prior_height;
  depth = gca->prior_depth;

  for (k = 0; k < depth; ++k)
    for (j= 0; j < height; ++j)
      for (i=0; i < width; ++i)
      {
        gcap = &gca->priors[i][j][k];
        // typedef struct
        // {
        //   short nlabels ;
        //   short max_labels ;        modify
        //   unsigned short  *labels ;  modify
        //   float *priors ;           modify
        //   int   total_training ;
        // } GCA_PRIOR ;
        //
        if (gcap)
        {
          n= gcap->nlabels;
          nmax = gcap->max_labels;
          if (n < nmax)
          {
            // printf("prior has more than needed (%d,%d,%d)
            // nlabels=%d, max_labels=%d\n", i,j,k, n, nmax);
            old_priors = gcap->priors;
            old_labels = gcap->labels;
            gcap->priors = (float *) calloc(n, sizeof(float));
            if (!gcap->priors)
              ErrorExit(ERROR_NOMEMORY,
                        "GCANupdatePriors: couldn't expand priors to %d",
                        gcap->max_labels) ;
            gcap->labels =
              (unsigned short *)calloc(n, sizeof(unsigned short)) ;
            if (!gcap->labels)
              ErrorExit(ERROR_NOMEMORY,
                        "GCANupdatePriors: couldn't expand labels to %d",
                        gcap->max_labels) ;
            /* copy the old ones over */
            memmove(gcap->priors, old_priors, n*sizeof(float)) ;
            memmove(gcap->labels, old_labels, n*sizeof(unsigned short)) ;

            /* free the old ones */
            free(old_priors) ;
            free(old_labels) ;
            gcap->max_labels = gcap->nlabels;

            byteSaved += (sizeof(float)+sizeof(unsigned short))*(nmax-n);
          }
        }
      }

  width = gca->node_width;
  height = gca->node_height;
  depth = gca->node_depth;
  for (k = 0; k < depth; ++k)
    for (j= 0; j < height; ++j)
      for (i=0; i < width; ++i)
      {
        gcan = &gca->nodes[i][j][k];
        if (gcan)
        {
          n= gcan->nlabels;
          nmax = gcan->max_labels;
          if (n < nmax)
          {
            // printf("node has more than needed (%d,%d,%d) "
            // "nlabels=%d, max_labels=%d\n", i,j,k, n, nmax);
            // typedef struct
            // {
            // int  nlabels ;
            // int  max_labels ;         modify
            // unsigned short *labels ;   modify
            // GC1D *gcs ;               modify
            // int  total_training ;
            /* total # of times this node was was accessed */
            // } GCA_NODE ;
            old_labels = gcan->labels;
            old_gcs = gcan->gcs;
            // only allocate what is needed
            gcan->gcs = alloc_gcs(n, gca->flags, gca->ninputs) ;
            if (!gcan->gcs)
              ErrorExit(ERROR_NOMEMORY,
                        "GCANupdateNode: couldn't expand gcs to %d",
                        gcan->max_labels) ;
            // only allocate what is needed
            gcan->labels =
              (unsigned short *)calloc(n, sizeof(unsigned short)) ;
            if (!gcan->labels)
              ErrorExit(ERROR_NOMEMORY,
                        "GCANupdateNode: couldn't expand labels to %d",
                        gcan->max_labels) ;
            copy_gcs(n, old_gcs, gcan->gcs, gca->ninputs);
            memmove(gcan->labels, old_labels, n*sizeof(unsigned short)) ;

            /* free the old ones */
            free(old_gcs) ;
            free(old_labels) ;
            gcan->max_labels = n;
            byteSaved += (sizeof(float)+sizeof(unsigned short))*(nmax-n);
          }
        }
      }

  if (DIAG_VERBOSE_ON)
    printf("GCAcompactify reduced the memory use by %.f bytes.\n", byteSaved);

  return gca;
}


MRI *
GCAreplaceImpossibleLabels(MRI *mri_inputs, GCA *gca,
                           MRI *mri_in_labels, MRI *mri_out_labels,
                           TRANSFORM *transform)
{
  int       x, y, z, width, height, depth, label,
  xn, yn, zn, n, found, nchanged ;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  float      max_p, p, vals[MAX_GCA_INPUTS] ;

  mri_out_labels = MRIcopy(mri_in_labels, mri_out_labels) ;

  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  for (nchanged = x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        label = nint(MRIgetVoxVal(mri_out_labels, x, y, z,0)) ;
        if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                  transform, x, y, z, &xn, &yn, &zn))
        {
          gcan = &gca->nodes[xn][yn][zn] ;
          gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
          if (gcap==NULL)
            continue;
          for (found = n = 0 ; n < gcap->nlabels ; n++)
          {
            if (gcap->labels[n] == label)
            {
              found = 1 ;
              break ;
            }
          }
          if (found)
            continue ;
          load_vals(mri_inputs, x, y, z, vals, gca->ninputs);
          nchanged++ ;
          max_p =
            GCAcomputePosteriorDensity(gcap, gcan, 0,
                                       vals, gca->ninputs) ;
          label = gcap->labels[0] ;
          for (n = 1 ; n < gcap->nlabels ; n++)
          {
            p = GCAcomputePosteriorDensity(gcap, gcan, n,
                                           vals, gca->ninputs) ;
            if (p >= max_p)
            {
              max_p = p ;
              label = gcap->labels[n] ;
            }
          }
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            printf("changing label at (%d, %d, %d) from %s (%d) to %s (%d)\n",
                   x, y, z,
                   cma_label_to_name(nint(MRIgetVoxVal(mri_out_labels,x,y,z,0))),
                   nint(MRIgetVoxVal(mri_out_labels,x,y,z,0)),
                   cma_label_to_name(label), label) ;
          MRIsetVoxVal(mri_out_labels, x, y, z, 0, label) ;
        }
      }
    }
  }

  printf("%d impossible labels replaced...\n", nchanged) ;
  return(mri_out_labels) ;
}



double
compute_partial_volume_log_posterior(GCA *gca,
                                     GCA_NODE *gcan,
                                     GCA_PRIOR *gcap,
                                     float *vals, int l1, int l2)
{
  GC1D *gc1, *gc2 ;
  int  i ;
  double p1, p2, p, alpha, u, v, p_alpha, dist ;

  gc1 = gc2 = NULL ;
  for (i = 0 ; i < gcan->nlabels ; i++)
  {
    if (gcan->labels[i] == l1)
      gc1 = &gcan->gcs[i] ;
    else if (gcan->labels[i] == l2)
      gc2 = &gcan->gcs[i] ;
  }
  if (gc1 == NULL || gc2 == NULL)
    return(VERY_UNLIKELY) ;

  p1 = p2 = 0 ;
  for (i = 0 ; i < gcap->nlabels ; i++)
  {
    if (gcap->labels[i] == l1)
      p1 = gcap->priors[i] ;
    else if (gcap->labels[i] == l2)
      p2 = gcap->priors[i] ;

  }

#define D_ALPHA 0.01
  for (p = alpha = 0.0 ; alpha <= 1.0 ; alpha += D_ALPHA)
  {
    u = alpha*gc1->means[0] + (1-alpha)*gc2->means[0] ;
    v = alpha*gc1->covars[0] + (1-alpha)*gc2->covars[0] ;
    dist = SQR(u-vals[0]) / v ;
    p_alpha = 1.0 / sqrt(v) * exp(-0.5*dist) *
              pow(p1,alpha)*pow(p2,1-alpha) ;
    p += (p_alpha*D_ALPHA) ;
  }
  return(log(p)) ;
}


static int
gcaRelabelSegment(GCA *gca, TRANSFORM *transform, MRI *mri_inputs,
                  MRI *mri_dst, MRI_SEGMENT *mseg)
{
  int         i, n, x, y, z, labels[MAX_CMA_LABEL+1],
  label, max_label, old_label, debug = 0 ;
  double      max_ll, new_ll ;
  GCA_PRIOR   *gcap ;

  memset(labels, 0, sizeof(labels)) ;

  /* build a list of all possible labels that could occur within this segment,
     and also compute current log likelihood */
  for (max_ll = 0.0, old_label = max_label = i = 0 ; i < mseg->nvoxels ; i++)
  {
    x = mseg->voxels[i].x ;
    y = mseg->voxels[i].y ;
    z = mseg->voxels[i].z ;
    if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
      debug = 1 ;
    max_ll += gcaNbhdGibbsLogLikelihood(gca, mri_dst, mri_inputs,
                                        x, y, z, transform, PRIOR_FACTOR) ;

    if (i == 0)
      old_label = max_label =
                    MRIgetVoxVal(mri_dst, x, y, z, 0) ; // current label
    gcap = getGCAP(gca, mri_dst, transform, x, y, z) ;
    for (n = 0 ; n < gcap->nlabels ; n++)
      labels[gcap->labels[n]]++ ;
    /* count # of possible labels in segment */
  }

  for (label = 0 ; label <= MAX_CMA_LABEL ; label++)
  {
    if (labels[label] <= 0)  /* not possible in this nbhd */
      continue ;

    /* change all labels to the new one */
    for (i = 0 ; i < mseg->nvoxels ; i++)
    {
      x = mseg->voxels[i].x ;
      y = mseg->voxels[i].y ;
      z = mseg->voxels[i].z ;
      MRIsetVoxVal(mri_dst, x, y, z, 0, label) ;
    }
    for (new_ll = 0.0, i = 0 ; i < mseg->nvoxels ; i++)
    {
      x = mseg->voxels[i].x ;
      y = mseg->voxels[i].y ;
      z = mseg->voxels[i].z ;
      new_ll += gcaNbhdGibbsLogLikelihood(gca, mri_dst, mri_inputs,
                                          x, y, z, transform,
                                          PRIOR_FACTOR) ;
    }
    if (new_ll > max_ll)
    {
      max_label = label ;
      max_ll = new_ll ;
    }
  }
  if ((old_label != max_label) && (debug || getenv("DEBUG_MRM")))
    printf("changing segment at (%2.0f, %2.0f, %2.0f) "
           "from %s (%d) to %s (%d)\n",
           mseg->cx, mseg->cy, mseg->cz,
           cma_label_to_name(old_label), old_label,
           cma_label_to_name(max_label), max_label) ;
  for (i = 0 ; i < mseg->nvoxels ; i++)
  {
    x = mseg->voxels[i].x ;
    y = mseg->voxels[i].y ;
    z = mseg->voxels[i].z ;
    MRIsetVoxVal(mri_dst, x, y, z, 0, max_label) ;
  }
  return(old_label != max_label) ;
}



MRI *
GCAmarkImpossible(GCA *gca,
                  MRI *mri_labeled,
                  MRI *mri_dst,
                  TRANSFORM *transform)
{
  int   x, y, z, label ;

  if (mri_dst == NULL)
    mri_dst = MRIclone(mri_labeled, NULL) ;
  for (x = 0 ; x < mri_labeled->width ; x++)
  {
    for (y = 0 ; y < mri_labeled->height ; y++)
    {
      for (z = 0 ; z < mri_labeled->depth ; z++)
      {
        label = MRIgetVoxVal(mri_labeled, x, y, z, 0) ;
        if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
          DiagBreak() ;
        if (GCAisPossible(gca, mri_labeled,
                          label, transform, x, y, z,1) ==0)
        {
          MRIsetVoxVal(mri_dst, x, y, z, 0, 1) ;
        }
      }
    }
  }
  return(mri_dst) ;
}


int
GCAmaxLabel(GCA *gca)
{
  int       x, y, z, max_label, n ;
  GCA_PRIOR *gcap ;

  max_label = 0 ;
  for (x = 0 ; x < gca->prior_width ; x++)
  {
    for (y = 0 ; y < gca->prior_height ; y++)
    {
      for (z = 0 ; z < gca->prior_depth ; z++)
      {
        gcap = &gca->priors[x][y][z] ;
        for (n = 0 ; n < gcap->nlabels ; n++)
        {
          if (gcap->labels[n] > max_label)
            max_label = gcap->labels[n] ;
        }
      }
    }
  }
  return(max_label) ;
}


MRI *
GCAbuildMostLikelyVolumeForStructure(GCA *gca, MRI *mri, int label, int border,
                                     TRANSFORM *transform, MRI *mri_labels)
{
  int              x,  y, z, xn, yn, zn, width, depth, height, 
    n, xp, yp, zp, r ;
  GCA_NODE         *gcan ;
  GCA_PRIOR        *gcap ;
  double           max_prior ;
  int              max_label ;
  GC1D             *gc_max ;
  MRI              *mri_tmp ;
  MRI_SEGMENTATION *mriseg ;

  if (!mri)
  {
    mri = MRIallocSequence(gca->prior_width, gca->prior_height,
                           gca->prior_depth, MRI_FLOAT, gca->ninputs) ;
    // hey create gca volume and thus copies gca prior values
    mri->xsize = gca->xsize*gca->prior_spacing;
    mri->ysize = gca->ysize*gca->prior_spacing;
    mri->zsize = gca->zsize*gca->prior_spacing;
  }
  // most likely volume should agree with direction cosines
  //  GCAcopyDCToMRI(gca, mri);

  if (mri->nframes != gca->ninputs)
    ErrorExit(ERROR_BADPARM, "GCAbuildMostLikelyVolume: mri->frames "
              "(%d) does not match gca->ninputs (%d)",
              mri->nframes, gca->ninputs) ;


  // mri is prior if mri = NULL
  width = mri->width ;
  depth = mri->depth ;
  height = mri->height ;
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        // get node value
        if (GCAsourceVoxelToNode
            (gca, mri, transform, x, y, z, &xn, &yn, &zn) == NO_ERROR)
        {
          // get prior value
          if (GCAsourceVoxelToPrior(gca, mri, transform, x, y, z,
                                    &xp, &yp, &zp) == NO_ERROR)
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            gcap = &gca->priors[xp][yp][zp] ;
            if (gcap==NULL || gcap->nlabels <= 0)
              continue;
            // initialize
            max_prior = gcap->priors[0] ;
            max_label = gcap->labels[0] ;
            gc_max = NULL ;
            // prior labels
            for (n = 1 ; n < gcap->nlabels ; n++)
            {
              if (gcap->priors[n] >= max_prior)
              {
                max_prior = gcap->priors[n] ;
                max_label = gcap->labels[n] ;
              }
            }
            if (max_label != label)
            {
              if (mri_labels)
                MRIsetVoxVal(mri_labels, x, y, z, 0, 0) ;
              for (r = 0 ; r < gca->ninputs ; r++)
              {
                MRIsetVoxVal(mri, x, y, z, r, 0) ;
              }
              continue ;
            }
            // get max_prior, max_label
            // go through node labels
            for (n = 0 ; n < gcan->nlabels ; n++)
            {
              if (gcan->labels[n] == max_label)
                gc_max = &gcan->gcs[n] ;
            }

            if (!gc_max)
              continue ;
            if (mri_labels)
              MRIsetVoxVal(mri_labels, x, y, z, 0, label) ;
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              MRIsetVoxVal(mri, x, y, z, r, gc_max->means[r]) ;
            }
          }
          else
          {
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              MRIsetVoxVal(mri, x, y, z, r, 0) ;
            }
          }
        }
        else
        {
          for (r = 0 ; r < gca->ninputs ; r++)
          {
            MRIsetVoxVal(mri, x, y, z, r, 0) ;
          }
        }
      }
    }
  }

  mriseg = MRIsegment(mri, 1.0, 255.0) ;
  if (!mriseg)
  {
    ErrorPrintf(Gerror,"GCAmostLikelyVolumeForStructure: "
                "label %s segmentation failed", cma_label_to_name(label)) ;
  }
  else if (mriseg->nsegments > 1)  // use largest segment
  {
#if 0
    MRI *mri_tmp ;
    int index ;

    index = MRIsegmentMax(mriseg) ;

    printf("%d segments detected for structure, using largest (%d vox)\n",
           mriseg->nsegments, mriseg->segments[index].nvoxels) ;
    if (mri_labels)
    {
      mri_tmp = MRIsegmentToImage(mri_labels, NULL, mriseg, index) ;
      MRIcopy(mri_tmp, mri_labels) ;
      MRIfree(&mri_tmp) ;
    }
    mri_tmp = MRIsegmentToImage(mri, NULL, mriseg, index) ;
    MRIcopy(mri_tmp, mri) ;
    MRIfree(&mri_tmp) ;
#endif
  }
  MRIsegmentFree(&mriseg) ;

  // add voxels from labels on the border of this stuct
  if (border > 0)
  {
    mri_tmp = MRIcopy(mri, NULL) ;

    for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
      {
        for (x = 0 ; x < width ; x++)
        {
          if (x == Gx && y == Gy && z == Gz)
            DiagBreak() ;
          if (MRIgetVoxVal(mri, x, y, z, 0) > 0)
            continue ;  // already filled in

          if (MRIareNonzeroInNbhd(mri, (2*border)+1, x, y, z) == 0)
            continue ;

          // get node value
          if (GCAsourceVoxelToNode
              (gca, mri, transform,
               x, y, z, &xn, &yn, &zn) == NO_ERROR)
          {
            // get prior value
            if (GCAsourceVoxelToPrior(gca, mri, transform, x, y, z,
                                      &xp, &yp, &zp) == NO_ERROR)
            {
              gcan = &gca->nodes[xn][yn][zn] ;
              gcap = &gca->priors[xp][yp][zp] ;
              if (gcap==NULL || gcap->nlabels <= 0)
                continue;
              // initialize
              max_prior = gcap->priors[0] ;
              max_label = gcap->labels[0] ;
              gc_max = NULL ;
              // prior labels
              for (n = 1 ; n < gcap->nlabels ; n++)
              {
                if (gcap->priors[n] >= max_prior)
                {
                  max_prior = gcap->priors[n] ;
                  max_label = gcap->labels[n] ;
                }
              }
              // get max_prior, max_label
              // go through node labels
              for (n = 0 ; n < gcan->nlabels ; n++)
              {
                if (gcan->labels[n] == max_label)
                  gc_max = &gcan->gcs[n] ;
              }

              if (!gc_max || max_prior < .25)
                continue ;
              if (mri_labels)
                MRIsetVoxVal(mri_labels, x, y, z, 0, max_label) ;
              for (r = 0 ; r < gca->ninputs ; r++)
              {
                MRIsetVoxVal
                (mri_tmp, x, y, z, r, gc_max->means[r]) ;
              }
            }
          }
        }
      }
    }

    MRIcopy(mri_tmp, mri) ;
    MRIfree(&mri_tmp) ;
  }
  return(mri) ;
}


#if 0
static float
gcaFindCerebellarScaleFactor(GCA *gca,
                             HISTOGRAM *h_mri,
                             int label, FILE *logfp)
{
  HISTOGRAM *h_gca ;
  int       xn, yn, zn, n, r ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  float     prior ;
  int       b, gca_peak1, gca_peak2, mri_peak1, mri_peak2 ;
  float     scale, mri_val1, mri_val2, gca_val1, gca_val2 ;

  // build histogram of this label
  h_gca = HISTOalloc(256) ;
  for (b = 0 ; b < h_gca->nbins ; b++)
    h_gca->bins[b] = b ;
  for (zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          /* find index in lookup table for this label */
          if (gcan->labels[n] != label)
            continue ;
          gc = &gcan->gcs[n] ;
          prior = get_node_prior(gca, label, xn, yn, zn) ;
          if (prior != 0)
          {
            for (r = 0 ; r < gca->ninputs ; r++)
            {
              b = nint(gc->means[r]) ;
              h_gca->counts[b] += prior ;
              if (!finite(gc->means[r]))
                DiagBreak() ;
            }
          }
        }
      }
    }
  }

  gca_peak1 = HISTOfindHighestPeakInRegion(h_gca, 0, h_gca->nbins) ;
  for (b = gca_peak1-2 ; b <= gca_peak1+2 ; b++)
    h_gca->counts[b] = 0 ;
  gca_peak2 = HISTOfindHighestPeakInRegion(h_gca, 0, h_gca->nbins) ;
  if (gca_peak1 < gca_peak2)
  {
    int p ;
    p = gca_peak1 ;
    gca_peak1 = gca_peak2 ;
    gca_peak2 = p ;
  }

  mri_peak1 = HISTOfindHighestPeakInRegion(h_mri, 0, h_mri->nbins) ;
  for (b = mri_peak1-2 ; b <= mri_peak1+2 ; b++)
    h_mri->counts[b] = 0 ;
  mri_peak2 = HISTOfindHighestPeakInRegion(h_mri, 0, h_mri->nbins) ;
  if (mri_peak1 < mri_peak2)
  {
    int p ;
    p = mri_peak1 ;
    mri_peak1 = mri_peak2 ;
    mri_peak2 = p ;
  }

  gca_val1 = h_gca->bins[gca_peak1] ;
  gca_val2 = h_gca->bins[gca_peak2] ;

  mri_val1 = h_mri->bins[mri_peak1] ;
  mri_val2 = h_mri->bins[mri_peak2] ;

  scale = (mri_val1/gca_val1 + mri_val2/gca_val2)/2 ;

  if (Gdiag & DIAG_SHOW)
    printf("%s (%d): peaks at %2.2f, %2.2f (%2.1f, %2.1f) "
           "scaling by %2.2f\n",
           cma_label_to_name(label), label, mri_val1, mri_val2,
           gca_val1, gca_val2, scale) ;
  if (logfp)
  {
    fprintf(logfp, "%s (%d): peaks at %2.2f, %2.2f (%2.1f, %2.1f) "
            "scaling by %2.2f\n",
            cma_label_to_name(label), label, mri_val1, mri_val2,
            gca_val1, gca_val2, scale) ;
    fflush(logfp) ;
  }

  return(scale) ;
}


static HISTOGRAM *
gcaComputeHistogramNormalization(GCA *gca, HISTOGRAM *h_mri, int label)
{
  HISTOGRAM *h_gca, *h_gca_eq, *h_mri_eq, *h_norm ;

  h_gca = gcaGetLabelHistogram(gca, label, 0) ;
  h_norm = HISTOcomposeInvert(h_gca, h_mri, NULL) ;
  HISTOfree(&h_gca) ;
  return(h_eq) ;
}

#endif
static HISTOGRAM *
gcaGetLabelHistogram(GCA *gca, int label, int frame)
{
  HISTOGRAM *h_gca ;
  int       xn, yn, zn, n ;
  GCA_NODE  *gcan ;
  GC1D      *gc ;
  float     prior ;
  int       b ;


  // build histogram of this label
  h_gca = HISTOalloc(256) ;
  for (b = 0 ; b < h_gca->nbins ; b++)
    h_gca->bins[b] = b ;
  for (zn = 0 ; zn < gca->node_depth ; zn++)
  {
    for (yn = 0 ; yn < gca->node_height ; yn++)
    {
      for (xn = 0 ; xn < gca->node_width ; xn++)
      {
        gcan = &gca->nodes[xn][yn][zn] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          /* find index in lookup table for this label */
          if (gcan->labels[n] != label)
            continue ;
          gc = &gcan->gcs[n] ;
          prior = get_node_prior(gca, label, xn, yn, zn) ;
          if (prior != 0)
          {
            //  for (r = 0 ; r < gca->ninputs ; r++)
            {
              b = nint(gc->means[frame]) ;
              if (b < 0 || b >= h_gca->nbins)
              {
                DiagBreak() ;
                if (b < 0)
                  b = 0 ;
                if (b >= h_gca->nbins)
                  b = h_gca->nbins - 1 ;
              }
              h_gca->counts[b] += prior ;
              if (!finite(gc->means[frame]))
                DiagBreak() ;
            }
          }
        }
      }
    }
  }
  return(h_gca) ;
}


#if INTERP_PRIOR
static float
gcaComputePrior(GCA *gca, MRI *mri, TRANSFORM *transform,
                int x0, int y0, int z0, int label)
{
  Real  x, y, z, xmd, ymd, zmd, xpd, ypd, zpd, prior ;
  int   xm, ym, zm, xp, yp, zp ;
  GCA_PRIOR *gcap ;
  float  total_prior  ;

  if (x0 == Ggca_x && y0 == Ggca_y && z0 == Ggca_z)
    DiagBreak() ;

  gcaSourceVoxelToPriorReal(gca, mri, transform, x0, y0, z0, &x, &y, &z);
  xm = MAX((int)x, 0) ;
  xp = MIN(gca->prior_width-1, xm+1) ;
  ym = MAX((int)y, 0) ;
  yp = MIN(gca->prior_height-1, ym+1) ;
  zm = MAX((int)z, 0) ;
  zp = MIN(gca->prior_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) ;

  gcap = &gca->priors[xp][yp][zp] ;
  prior  = getPrior(gcap, label) ;
  total_prior = prior*xpd*ypd*zpd ;

  gcap = &gca->priors[xp][yp][zm] ;
  prior  = getPrior(gcap, label) ;
  total_prior += prior*xpd*ypd*zmd ;

  gcap = &gca->priors[xp][ym][zp] ;
  prior  = getPrior(gcap, label) ;
  total_prior += prior*xpd*ymd*zpd ;

  gcap = &gca->priors[xp][ym][zm] ;
  prior  = getPrior(gcap, label) ;
  total_prior += prior*xpd*ymd*zmd ;

  gcap = &gca->priors[xm][yp][zp] ;
  prior  = getPrior(gcap, label) ;
  total_prior += prior*xmd*ypd*zpd ;

  gcap = &gca->priors[xm][yp][zm] ;
  prior  = getPrior(gcap, label) ;
  total_prior += prior*xmd*ypd*zmd ;

  gcap = &gca->priors[xm][ym][zp] ;
  prior  = getPrior(gcap, label) ;
  total_prior += prior*xmd*ymd*zpd ;

  gcap = &gca->priors[xm][ym][zm] ;
  prior  = getPrior(gcap, label) ;
  total_prior += prior*xmd*ymd*zmd ;

  return(total_prior) ;
}

#endif



static void  set_equilavent_classes(int *equivalent_classes)
{
  int i;

  for (i=0; i < MAX_CMA_LABELS; i++)
  {
    equivalent_classes[i] = i;
  }

  //CSF 1, GM 3, WM 2
  equivalent_classes[0] = 0;
  equivalent_classes[1] = 1;
  equivalent_classes[2] = 2;
  equivalent_classes[3] = 3;
  equivalent_classes[4] = 1;
  equivalent_classes[5] = 1;
  equivalent_classes[6] = 0;
  equivalent_classes[7] = 2;
  equivalent_classes[8] = 3;
  equivalent_classes[9] = 3;
  equivalent_classes[10] = 3;
  equivalent_classes[11] = 3;
  equivalent_classes[12] = 3;
  equivalent_classes[13] = 3;
  equivalent_classes[14] = 1;
  equivalent_classes[15] = 1;
  equivalent_classes[16] = 2; //this is brain stem, really WM??
  equivalent_classes[17] = 3;
  equivalent_classes[18] = 3;
  equivalent_classes[19] = 0; //Left_Insula
  equivalent_classes[20] = 0; //Left_Operculum
  equivalent_classes[21] = 0; //Line_1
  equivalent_classes[22] = 0; //Line_2
  equivalent_classes[23] = 0; //Line_3
  equivalent_classes[24] = 1; //CSF
  equivalent_classes[25] = 0; //Left_lesion
  equivalent_classes[26] = 3; //left_accumbens_area
  equivalent_classes[27] = 0; //Left_Substancia_Nigra
  equivalent_classes[28] = 0; //Left_VentralDC;
  equivalent_classes[29] = 0; //left_undetermined
  equivalent_classes[30] = 0; // Left_vessel
  equivalent_classes[31] = 0; //left_choroid_plexus
  equivalent_classes[32] = 0; //lEFT_f3ORB
  equivalent_classes[33] = 0; //Left_lOg
  equivalent_classes[34] = 0; //Left_aOg
  equivalent_classes[35] = 0; // Left_mOg
  equivalent_classes[36] = 0; //Left_pOg
  equivalent_classes[37] = 0; //Left_Stellate
  equivalent_classes[38] = 0; //Left_Porg
  equivalent_classes[39] = 0; //Left_Aorg
  equivalent_classes[40] = equivalent_classes[1];
  equivalent_classes[41] = equivalent_classes[2];
  equivalent_classes[42] = equivalent_classes[3];
  equivalent_classes[43] =equivalent_classes[4];
  equivalent_classes[44] =equivalent_classes[5];
  equivalent_classes[45] =equivalent_classes[6];
  equivalent_classes[46] =equivalent_classes[7];
  equivalent_classes[47] =equivalent_classes[8];
  equivalent_classes[48] =equivalent_classes[9];
  equivalent_classes[49] =equivalent_classes[10];
  equivalent_classes[50] =equivalent_classes[11];
  equivalent_classes[51] =equivalent_classes[12];
  equivalent_classes[52] =equivalent_classes[13];
  equivalent_classes[53] =equivalent_classes[17];
  equivalent_classes[54] =equivalent_classes[18];
  equivalent_classes[55] =equivalent_classes[19];
  equivalent_classes[56] =equivalent_classes[20];
  equivalent_classes[57] =equivalent_classes[25];
  equivalent_classes[58] =equivalent_classes[26];
  equivalent_classes[59] =equivalent_classes[27];
  equivalent_classes[60] =equivalent_classes[28];
  equivalent_classes[61] =equivalent_classes[29];
  equivalent_classes[62] =equivalent_classes[30];
  equivalent_classes[63] =equivalent_classes[31]; //choroid_plexus
  equivalent_classes[64] =equivalent_classes[32];
  equivalent_classes[65] =equivalent_classes[33];
  equivalent_classes[66] =equivalent_classes[34];
  equivalent_classes[67] =equivalent_classes[35];
  equivalent_classes[68] =equivalent_classes[36];
  equivalent_classes[69] =equivalent_classes[37];
  equivalent_classes[70] =equivalent_classes[38];
  equivalent_classes[71] =equivalent_classes[39];
  equivalent_classes[72] =1;
  equivalent_classes[73] =0; // Left_Interior
  equivalent_classes[74] =equivalent_classes[73];
  equivalent_classes[75] = 1;
  equivalent_classes[76] =equivalent_classes[75];
  equivalent_classes[77] = 2;
  equivalent_classes[78] = 2;
  equivalent_classes[79] =equivalent_classes[78];
  equivalent_classes[80] = 2;
  equivalent_classes[81] = 2;
  equivalent_classes[82] =equivalent_classes[81];
  equivalent_classes[83] = 0;
  equivalent_classes[84] =equivalent_classes[83];
  equivalent_classes[186] = 2;
  equivalent_classes[187] =equivalent_classes[186];

  return;
}


MRI *GCAbuildMostLikelyLabelVolume(GCA *gca)
{
  /* this function creates a label volume and will be used to register
     a subject's manual label to it, as a way to get linear registration
     from the subject to the gca for gca training */
  int       x,  y, z, xn, yn, zn, width, depth, height, n, xp, yp, zp;
  GCA_NODE  *gcan ;
  GCA_PRIOR *gcap ;
  MRI *mri;
  double    max_prior ;
  int       max_label ;

  // most likely label volume should agree with direction cosines
  mri = MRIalloc(gca->width, gca->height, gca->depth, MRI_SHORT);

	mri->xsize = gca->xsize;
	mri->ysize = gca->ysize;
	mri->zsize = gca->zsize;
  GCAcopyDCToMRI(gca, mri);

  width = mri->width ; depth = mri->depth ;height = mri->height ;
  for (z = 0 ; z < depth ; z++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (x = 0 ; x < width ; x++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        // get node value
        if (GCAvoxelToNode(gca, mri, x, y, z, &xn, &yn, &zn) == NO_ERROR)
        {
          // get prior value
          if (GCAvoxelToPrior(gca, mri, x, y, z,
                              &xp, &yp, &zp) == NO_ERROR)
          {
            gcan = &gca->nodes[xn][yn][zn] ;
            gcap = &gca->priors[xp][yp][zp] ;
            if (gcap==NULL || gcap->nlabels <= 0)
              continue;
            // initialize
            max_prior = gcap->priors[0] ;
            max_label = gcap->labels[0] ;
            // prior labels
            for (n = 1 ; n < gcap->nlabels ; n++)
            {
              if (gcap->priors[n] >= max_prior)
              {
                max_prior = gcap->priors[n] ;
                max_label = gcap->labels[n] ;
              }
            }
            MRIsetVoxVal(mri, x, y, z, 0, max_label) ;
          }
          else
          {
            MRIsetVoxVal(mri, x, y, z, 0, 0) ;
          }
        }
        else
        {
          MRIsetVoxVal(mri, x, y, z, 0, 0) ;

        }
      }
    }
  }

  return(mri) ;
}



int
GCAcomputeLabelMeansAndCovariances
(GCA *gca, int target_label, MATRIX **p_mcov, VECTOR **p_vmeans)
{
  int      x, y, z, n, r ;
  double   var, dof, total_dof ;
  GC1D     *gc ;
  GCA_NODE *gcan ;
  float fval;
  MATRIX *m_cov=NULL, *m_cov_total ;
  VECTOR *v_means ;

  m_cov_total = MatrixAlloc(gca->ninputs, gca->ninputs, MATRIX_REAL) ;
  v_means = VectorAlloc(gca->ninputs, MATRIX_REAL) ;

  var = total_dof = 0.0 ;
  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;

        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (gcan->labels[n] == target_label)
          {
            gc = &gcan->gcs[n] ;
            fval = get_node_prior(gca, target_label, x,y,z);
            if (fval != 0)
            {
              dof =
                get_node_prior(gca, target_label, x, y, z) \
                * gcan->total_training ;
              for (r = 0 ; r < gca->ninputs ; r++)
                VECTOR_ELT(v_means, r+1) += dof*gc->means[r] ;
              m_cov = load_covariance_matrix
                      (gc, m_cov, gca->ninputs) ;
              MatrixScalarMul(m_cov, dof, m_cov) ;
              MatrixAdd(m_cov, m_cov_total, m_cov_total) ;
              total_dof += dof ;
            }
          }
        }
      }
    }
  }

  if (total_dof > 0.0)
  {
    MatrixScalarMul(m_cov_total, 1/(double)total_dof, m_cov_total) ;
    VectorScalarMul(v_means, 1/(double)total_dof, v_means) ;
  }

  *p_mcov = m_cov_total ;
  *p_vmeans = v_means ;
  MatrixFree(&m_cov) ;
  return(NO_ERROR) ;
}
#ifdef WSIZE
#undef WSIZE
#endif
#ifdef WHALF
#undef WHALF
#endif

#define WSIZE  5
#define WHALF  ((WSIZE-1)/2)
static double
compute_conditional_density(MATRIX *m_inv_cov, VECTOR *v_means, VECTOR *v_vals)
{
  double  p, dist, det ;
  int     ninputs ;

  ninputs = m_inv_cov->rows ;

  det = MatrixDeterminant(m_inv_cov) ;
  dist = MatrixMahalanobisDistance(v_means, m_inv_cov, v_vals) ;
  p = (1.0 / (pow(2*M_PI,ninputs/2.0)*sqrt(1.0/det))) * exp(-0.5*dist) ;
  return(p) ;
}


static int
load_val_vector(VECTOR *v_means, MRI *mri_inputs, int x, int y, int z)
{
  int  n ;

  for (n = 0 ; n < mri_inputs->nframes ; n++)
    VECTOR_ELT(v_means, n+1) = MRIgetVoxVal(mri_inputs, x, y, z, n) ;
  return(NO_ERROR) ;
}


MRI *
GCAlabelWMandWMSAs(GCA *gca,
                   MRI *mri_inputs,
                   MRI *mri_src_labels,
                   MRI *mri_dst_labels,
                   TRANSFORM *transform)
{
  int    h, wm_label, wmsa_label, x, y, z, label, nwm, nwmsa, nunknown, ngm,
  ncaudate, caudate_label, gm_label, n, found, i;
  MATRIX *m_cov_wm, *m_cov_wmsa, *m_inv_cov_wmsa, *m_inv_cov_wm, *m_I,
  *m_cov_un, *m_inv_cov_un;
  VECTOR *v_mean_wm, *v_mean_wmsa, *v_vals, *v_dif_label, *v_dif_wmsa,
  *v_mean_caudate, *v_mean_un ;
  double pwm, pwmsa, wmsa_dist, wm_dist, wm_mdist, wmsa_mdist ;
  GCA_PRIOR *gcap ;
  MRI       *mri_tmp = NULL ;

  mri_dst_labels = MRIcopy(mri_src_labels, mri_dst_labels) ;

  v_vals = VectorAlloc(mri_inputs->nframes, MATRIX_REAL) ;
  v_dif_label = VectorAlloc(mri_inputs->nframes, MATRIX_REAL) ;
  v_dif_wmsa = VectorAlloc(mri_inputs->nframes, MATRIX_REAL) ;
  m_I = MatrixIdentity(mri_inputs->nframes, NULL) ;
  for (h = 0 ; h <= 1 ; h++)
  {
    if (h == 0) // lh
    {
      wm_label = Left_Cerebral_White_Matter ;
      wmsa_label = Left_WM_hypointensities ;
      caudate_label = Left_Caudate ;
      gm_label = Left_Cerebral_Cortex ;
    }
    else
    {
      wm_label = Right_Cerebral_White_Matter ;
      wmsa_label = Right_WM_hypointensities ;
      caudate_label = Right_Caudate ;
      gm_label = Right_Cerebral_Cortex ;
    }

    GCAcomputeLabelMeansAndCovariances
    (gca, Unknown, &m_cov_un, &v_mean_un) ;
    GCAcomputeLabelMeansAndCovariances
    (gca, wm_label, &m_cov_wm, &v_mean_wm) ;
    GCAcomputeLabelMeansAndCovariances
    (gca, caudate_label, &m_cov_wm, &v_mean_caudate) ;
    GCAcomputeLabelMeansAndCovariances
    (gca, wmsa_label, &m_cov_wmsa, &v_mean_wmsa) ;
    m_inv_cov_wm = MatrixInverse(m_cov_wm, NULL) ;
    if (m_inv_cov_wm == NULL)
      ErrorExit(ERROR_BADPARM,
                "%s: could not compute inverse covariance for %s (%d)",
                Progname, cma_label_to_name(wm_label), wm_label) ;
    m_inv_cov_un = MatrixInverse(m_cov_un, NULL) ;
    if (m_inv_cov_un == NULL)
      ErrorExit(ERROR_BADPARM,
                "%s: could not compute inverse covariance for %s (%d)",
                Progname, cma_label_to_name(Unknown), Unknown) ;
    m_inv_cov_wmsa = MatrixInverse(m_cov_wmsa, NULL) ;
    if (m_inv_cov_wmsa == NULL)
      ErrorExit(ERROR_BADPARM,
                "%s: could not compute inverse covariance for %s (%d)",
                Progname, cma_label_to_name(wmsa_label), wmsa_label) ;

    // do max likelihood reclassification of possible wmsa voxels
    // if they are in a nbhd with likely labels
    for (x = 0 ; x < mri_inputs->width ; x++)
    {
      for (y = 0 ; y < mri_inputs->height ; y++)
      {
        for (z = 0 ; z < mri_inputs->depth ; z++)
        {
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            DiagBreak() ;
          label = MRIgetVoxVal(mri_src_labels, x, y, z, 0) ;
          if (label != wm_label &&
              label != wmsa_label &&
              label != Unknown)
            continue ;
          // only process it if it's in the body of the wm
          nwm = MRIlabelsInNbhd
                (mri_src_labels, x, y, z, WHALF, wm_label) ;
          nwmsa = MRIlabelsInNbhd
                  (mri_src_labels, x, y, z,WHALF, wmsa_label) ;

          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            printf("(%d, %d, %d) - %s (nbrs = %d + %d = %2.2f%%)\n",
                   x, y, z, cma_label_to_name(label),
                   nwm, nwmsa,
                   (double)(nwm+nwmsa)*100.0/(WSIZE*WSIZE*WSIZE));
          if (label == Unknown)
          {
            // only unknowns that are close to wm
            if (nwm+nwmsa < 0.5*WSIZE*WSIZE*WSIZE)
              continue ;
            nunknown = MRIlabelsInNbhd
                       (mri_src_labels, x, y, z,WHALF, Unknown) ;
            if (nwm+nwmsa+nunknown < 0.9*WSIZE*WSIZE*WSIZE)
              continue ;
          }
          else if (nwm+nwmsa < .9*WSIZE*WSIZE*WSIZE)
            // somewhat arbitrary - the bulk of the nbhd
            continue ;

          gcap = getGCAP(gca, mri_dst_labels, transform, x, y, z) ;
          for (found = n = 0 ; n < gcap->nlabels ; n++)
            if ((IS_WHITE_CLASS(gcap->labels[n]) &&
                 gcap->priors[n] > 0.1) ||
                IS_HYPO(gcap->labels[n]))
              found = 1 ;
          if (found == 0)  // no chance of wm or wmsa here
            continue ;

          if (label == Unknown)
            DiagBreak() ;
          load_val_vector(v_vals, mri_inputs, x, y, z) ;
          pwm = compute_conditional_density
                (m_inv_cov_wm, v_mean_wm, v_vals) ;
          pwmsa = compute_conditional_density
                  (m_inv_cov_wmsa, v_mean_wmsa, v_vals) ;
          if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
            printf("         - pwm = %2.3e, pwmsa = %2.3e\n",
                   pwm, pwmsa) ;
          if (label == wm_label && pwmsa > pwm)
          {
            wm_dist = VectorDistance(v_mean_wm, v_vals) ;
            wmsa_dist = VectorDistance(v_mean_wmsa, v_vals) ;
            wm_mdist = MatrixMahalanobisDistance
                       (v_mean_wm, m_inv_cov_wm, v_vals) ;
            wmsa_mdist = MatrixMahalanobisDistance
                         (v_mean_wmsa, m_inv_cov_wmsa, v_vals) ;
            if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
              printf("         - wm_dist = %2.0f, "
                     "wmsa_dist = %2.0f, mdists = (%2.0f, %2.0f)\n",
                     wm_dist, wmsa_dist, wm_mdist, wmsa_mdist) ;
            if ((wm_dist > wmsa_dist) && (wm_mdist > wmsa_mdist))
            {
              VectorSubtract(v_vals, v_mean_wm, v_dif_label) ;
              VectorSubtract(v_vals, v_mean_wmsa, v_dif_wmsa) ;
              if (
                ((fabs(VECTOR_ELT(v_dif_wmsa,1)) <
                  fabs(VECTOR_ELT(v_dif_label,1))) &&
                 (fabs(VECTOR_ELT(v_dif_wmsa,2)) <
                  fabs(VECTOR_ELT(v_dif_label,2))) &&
                 (fabs(VECTOR_ELT(v_dif_wmsa,3)) <
                  fabs(VECTOR_ELT(v_dif_label,3)))) ||
                ((2*wmsa_dist < wm_dist) &&
                 (2*wmsa_mdist < wm_mdist)))
              {
                if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
                  printf("changing label from %s to %s\n",
                         cma_label_to_name(label),
                         cma_label_to_name(wmsa_label)) ;
                if (label == Unknown)
                  DiagBreak() ;
                label = wmsa_label ;
              }
            }
          }
          MRIsetVoxVal(mri_dst_labels, x, y, z, 0, label) ;
        }
      }
    }

    // now do 3 iterations of region growing
    for (i = 0 ; i < 3 ; i++)
    {
      mri_tmp = MRIcopy(mri_dst_labels, mri_tmp) ;
      for (x = 0 ; x < mri_inputs->width ; x++)
      {
        for (y = 0 ; y < mri_inputs->height ; y++)
        {
          for (z = 0 ; z < mri_inputs->depth ; z++)
          {
            if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
              DiagBreak() ;
            label = MRIgetVoxVal(mri_dst_labels, x, y, z, 0) ;
            if (label != wm_label &&
                label != Unknown &&
                label != caudate_label)
              continue ;
            load_val_vector(v_vals, mri_inputs, x, y, z) ;
            nwmsa = MRIlabelsInNbhd
                    (mri_dst_labels, x, y, z, 1, wmsa_label) ;
            if (nwmsa < 1)
              continue ;
            gcap = getGCAP(gca, mri_dst_labels, transform, x, y, z) ;
            for (found = n = 0 ; n < gcap->nlabels ; n++)
              if ((IS_WHITE_CLASS(gcap->labels[n]) &&
                   gcap->priors[n] > 0.1) ||
                  IS_HYPO(gcap->labels[n]))
                found = 1 ;
            if (found == 0)  // no chance of wm or wmsa here
              continue ;

            // only process it if it's in the body of the wm
#undef WSIZE
#define WSIZE 5
#define WHALF ((WSIZE-1)/2)

            nwm = MRIlabelsInNbhd
                  (mri_tmp, x, y, z, WHALF, wm_label) ;
            nwmsa = MRIlabelsInNbhd
                    (mri_tmp, x, y, z,WHALF, wmsa_label) ;
            nunknown = MRIlabelsInNbhd
                       (mri_tmp, x, y, z,WHALF, Unknown) ;
            ncaudate = MRIlabelsInNbhd
                       (mri_tmp, x, y, z,WHALF, caudate_label) ;
            ngm = MRIlabelsInNbhd
                  (mri_tmp, x, y, z,WHALF, gm_label) ;

            // took gm out for now
            if (ncaudate+nwm+nwmsa+nunknown <
                .9*WSIZE*WSIZE*WSIZE)  /* somewhat arbitrary -
                                          the bulk of the nbhd */
              continue ;
            ngm = MRIlabelsInNbhd(mri_tmp, x, y, z,1, gm_label) ;
            if (ngm > 0)  // not if there are any gm nearest nbrs
              continue ;

            if (nwm + nwmsa == 0)
              continue ;
            wm_dist = VectorDistance(v_mean_wm, v_vals) ;
            wmsa_dist = VectorDistance(v_mean_wmsa, v_vals) ;
            VectorSubtract(v_vals, v_mean_wmsa, v_dif_wmsa) ;
            if (label == caudate_label)
            {
              VectorSubtract(v_vals, v_mean_caudate, v_dif_label) ;
              if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
                printf("         - wm_dist = %2.0f, "
                       "wmsa_dist = %2.0f\n",
                       wm_dist, wmsa_dist) ;
              if ((fabs(VECTOR_ELT(v_dif_wmsa,1)) <
                   fabs(VECTOR_ELT(v_dif_label,1))) &&
                  (fabs(VECTOR_ELT(v_dif_wmsa,2)) <
                   fabs(VECTOR_ELT(v_dif_label,2))) &&
                  (fabs(VECTOR_ELT(v_dif_wmsa,3)) <
                   fabs(VECTOR_ELT(v_dif_label,3))))
              {
                if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
                  printf("changing label from %s to %s\n",
                         cma_label_to_name(label),
                         cma_label_to_name(wmsa_label)) ;
                label = wmsa_label ;
              }
            }
            else if (label == wm_label)
            {
              VectorSubtract(v_vals, v_mean_wm, v_dif_label) ;
              if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
                printf("         - wm_dist = %2.0f, "
                       "wmsa_dist = %2.0f\n",
                       wm_dist, wmsa_dist) ;
              if (((fabs(VECTOR_ELT(v_dif_wmsa,1)) <
                    fabs(VECTOR_ELT(v_dif_label,1))) &&
                   (fabs(VECTOR_ELT(v_dif_wmsa,2)) <
                    fabs(VECTOR_ELT(v_dif_label,2))) &&
                   (fabs(VECTOR_ELT(v_dif_wmsa,3)) <
                    fabs(VECTOR_ELT(v_dif_label,3)))) ||
                  (wmsa_dist*3 < wm_dist))
              {
                if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
                  printf("changing label from %s to %s\n",
                         cma_label_to_name(label),
                         cma_label_to_name(wmsa_label)) ;
                if (label == Unknown)
                  DiagBreak() ;
                label = wmsa_label ;
              }
            }
            else if (label == Unknown)
            {
              VectorSubtract(v_vals, v_mean_un, v_dif_label) ;
              if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
                printf("         - wm_dist = %2.0f, "
                       "wmsa_dist = %2.0f\n",
                       wm_dist, wmsa_dist) ;
              if ((fabs(VECTOR_ELT(v_dif_wmsa,1)) <
                   fabs(VECTOR_ELT(v_dif_label,1))) &&
                  (fabs(VECTOR_ELT(v_dif_wmsa,2)) <
                   fabs(VECTOR_ELT(v_dif_label,2))) &&
                  (fabs(VECTOR_ELT(v_dif_wmsa,3)) <
                   fabs(VECTOR_ELT(v_dif_label,3))))
              {
                if (x == Ggca_x && y == Ggca_y && z == Ggca_z)
                  printf("changing label from %s to %s\n",
                         cma_label_to_name(label),
                         cma_label_to_name(wmsa_label)) ;
                if (label == Unknown)
                  DiagBreak() ;
                label = wmsa_label ;
              }
            }
            MRIsetVoxVal(mri_dst_labels, x, y, z, 0, label) ;
          }
        }
      }
    }
    MatrixFree(&m_cov_un) ;
    MatrixFree(&m_inv_cov_un) ;
    MatrixFree(&m_cov_wm) ;
    MatrixFree(&m_inv_cov_wm) ;
    MatrixFree(&m_cov_wmsa) ;
    MatrixFree(&m_inv_cov_wmsa) ;
    VectorFree(&v_mean_wm) ;
    VectorFree(&v_mean_wmsa) ;
    VectorFree(&v_mean_caudate) ;
    VectorFree(&v_mean_un) ;
  }
  VectorFree(&v_vals) ;
  VectorFree(&v_dif_label) ;
  VectorFree(&v_dif_wmsa) ;
  MRIfree(&mri_tmp) ;
  return(mri_dst_labels) ;
}
static int
initialize_ventricle_alignment(MRI *mri_seg, 
                               MRI *mri, 
                               MATRIX *m_L, 
                               char *base_name)
{
  printf("Handling expanded ventricles... "
         "(gca::initialize_ventricle_alignment)\n");

  MRIcomputeOptimalLinearXform(mri_seg, mri, m_L,
                               -RADIANS(10), RADIANS(10),
                               .9, 2,  // allow for ventriular expansion
                               -10, 10,
                               4, 3, 3, 3, base_name) ;
  return(NO_ERROR) ;
}

/*-------------------------------------------------------------------------
  GCAcolorTableCMA() - construct a color table from the unique entries
  in the GCA.  RGBs are random. Indices that are not represented have
  their entries in the ctab set to NULL.  Note that the names in cma.h
  do not match the names in FreeSurferLUT.txt exactly, so
  FreeSurferLUT.txt is loaded and the names are extracted rather than
  using those in cma.h.
  -------------------------------------------------------------------------*/
COLOR_TABLE *GCAcolorTableCMA(GCA *gca)
{
  int nl,n,c,r,s,nmax;
  int labelhitlist[1000]; // probably only need 256
  COLOR_TABLE *ct,*ct0;
  char ctabfile[2000];

  sprintf(ctabfile,"%s/FreeSurferColorLUT.txt",getenv("FREESURFER_HOME"));
  printf("GCAcolorTableCMA: using ctab %s\n",ctabfile);
  ct0 = CTABreadASCII(ctabfile);
  if (ct0 == NULL)
  {
    printf("ERROR: reading %s\n",ctabfile);
    exit(1);
  }

  // Init the hit
  for (n=0; n<1000; n++) labelhitlist[n]=0;

  // Go thru each node
  for (c=0; c < gca->node_width; c++)
  {
    for (r=0; r < gca->node_height; r++)
    {
      for (s=0; s < gca->node_depth; s++)
      {
        nl = gca->nodes[c][r][s].nlabels;
        // Go thru each label in the node
        for (n=0;n<nl;n++)
        {
          // Get the index (labels[n] is an index, not string)
          // Mark the index as represented
          labelhitlist[gca->nodes[c][r][s].labels[n]] = 1;
        } //n
      } //s
    } //r
  } //c

  // determine the maximum index
  nmax = 0;
  for (n=0; n<1000; n++) if (labelhitlist[n]) nmax=n;

  ct = CTABalloc(nmax+1);
  for (n=0; n<=nmax; n++)
  {
    if (labelhitlist[n])
    {
      // If this index is represented, then set up its
      // entry in the color table. The name is derived
      // from the CMA.
      ct->entries[n]->ri = nint(randomNumber(0, 255));
      ct->entries[n]->gi = nint(randomNumber(0, 255));
      ct->entries[n]->bi = nint(randomNumber(0, 255));
      ct->entries[n]->ai = 255;
      ct->entries[n]->rf = (float)ct->entries[n]->ri / 255.0;
      ct->entries[n]->gf = (float)ct->entries[n]->gi / 255.0;
      ct->entries[n]->bf = (float)ct->entries[n]->bi / 255.0;
      ct->entries[n]->af = (float)ct->entries[n]->ai / 255.0;
      // There is a mismatch between the names listed in
      // FreeSurferColorLUT.txt and the names in cma.h but the
      // indices are the same (I hope), so we need to
      // use the names from FreeSurferColorLUT.txt.
      sprintf(ct->entries[n]->name, "%s", ct0->entries[n]->name);
      // printf("%d %s %s\n", n,cma_label_to_name(n),ct->entries[n]->name);
    }
    else
    {
      // If this index is not represented, then free and NULL
      // its entry.
      free(ct->entries[n]);
      ct->entries[n] = NULL;
    }
  }
  CTABfree(&ct0);
  return(ct);
}
#if 1
double
GCAimageLogLikelihood(GCA *gca, MRI *mri_inputs, TRANSFORM *transform,
                      int penalize_zero_brain, MRI *mri_orig)
{
  int        x, y, z, xn, yn, zn, label, xp, yp, zp, num,nz,nout, n ;
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  double     total_log_p, log_p=0, prior, min_log_p ;
  float      vals[MAX_GCA_INPUTS], fmin, fmax ;
  MRI        *mri_ll ;

  if (DIAG_VERBOSE_ON)
    mri_ll = MRIalloc(mri_inputs->width, 
                      mri_inputs->height, 
                      mri_inputs->depth, 
                      MRI_FLOAT) ;
  else
    mri_ll = NULL ;

  if (Gx >= 0)
  {
    GCAsourceVoxelToPrior(gca, mri_inputs,
                          transform, Gx, Gy, Gz, &Gxp, &Gyp, &Gzp);
    GCApriorToSourceVoxel(gca, mri_inputs, transform, Gxp, Gyp, Gzp,&x,&y,&z);
  }

  fmin = 100000 ;
  fmax = -fmin ;
  for (min_log_p = 0, nz = nout = num = xp = 0 ; xp < gca->prior_width ; xp++)
  {
    for (yp = 0 ; yp < gca->prior_height ; yp++)
    {
      for (zp = 0 ; zp < gca->prior_depth ; zp++)
      {
        if (xp == Gxp && yp == Gyp && zp == Gzp)
          DiagBreak() ;
        gcap = &gca->priors[xp][yp][zp] ;
        if (gcap == NULL || gcap->nlabels == 0)
          continue ;
        min_log_p += gcap->total_training ;
        label = gcapGetMaxPriorLabel(gcap, &prior) ;
        if (IS_BRAIN(label) && !IS_CSF(label))
        {
          GCApriorToNode(gca, xp, yp, zp, &xn, &yn, &zn) ;
          gc = GCAfindGC(gca, xn, yn, zn, label) ;
          if (gc->means[0] > fmax)
            fmax = gc->means[0] ;
          if (gc->means[0] < fmin)
            fmin = gc->means[0] ;
        }
      }
    }
  }
  fprintf(stderr,"valid intensity range = [%2.0f, %2.0f]\n", fmin, fmax) ;

  min_log_p = log(0.01/min_log_p) ;
  if (min_log_p > -50)
    min_log_p = -50 ;  // to penalize enough (I know it's a hack)
  for (total_log_p = 0, nz = num = xp = 0 ; xp < gca->prior_width ; xp++)
  {
    for (yp = 0 ; yp < gca->prior_height ; yp++)
    {
      for (zp = 0 ; zp < gca->prior_depth ; zp++)
      {
        if (xp == Gxp && yp == Gyp && zp == Gzp)
          DiagBreak() ;
        gcap = &gca->priors[xp][yp][zp] ;
        if (gcap == NULL || gcap->nlabels == 0)
        {
          num++ ;
          nout++ ;
          total_log_p += min_log_p ;
          continue ;
        }
        if (GCApriorToSourceVoxel(gca, mri_inputs, transform,
                                  xp, yp, zp,&x,&y,&z) != NO_ERROR)
        {
          num++ ;
          nout++ ;
          total_log_p += min_log_p ;
          continue ;
        }
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        GCApriorToNode(gca, xp, yp, zp, &xn, &yn, &zn) ;
        load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;
        for (n = 0 ; n < gcap->nlabels ; n++)
        {
          label = gcap->labels[n] ;
          prior = gcap->priors[n] ;
          gc = GCAfindGC(gca, xn, yn, zn, label) ;
          if (gc == NULL)
          {
            num++ ;
            nout++ ;
            total_log_p += min_log_p ;
            continue ;
          }
          //        if (IS_BRAIN(label) == 0 || FZERO(prior))
          if (FZERO(prior))
            continue ;
          num++ ;

          log_p = gcaComputeLogDensity(gc, vals,gca->ninputs,prior, label);
          if (penalize_zero_brain && 
              FZERO(vals[0]) && 
              IS_BRAIN(label) && 
              !IS_CSF(label))
          {
            if (mri_orig)  // use unstripped volume to 
                           // see if this could have been brain
            {
              float      ovals[MAX_GCA_INPUTS] ;
              load_vals(mri_orig, x, y, z, ovals, gca->ninputs) ;
              if (ovals[0] >= fmin && 
                  ovals[0] <= fmax)  // could have been brain
              {
                nz++ ;
                log_p += min_log_p ;
              }
            }
            else
            {
              log_p += min_log_p ;
              nz++ ;
            }
          }
        }
        if (mri_ll)
          MRIsetVoxVal(mri_ll, x, y, z, 0, log_p) ;
        if (!finite(log_p))
          DiagBreak() ;
        total_log_p += log_p ;
      }
    }
  }

  if (mri_ll)
    MRIwrite(mri_ll, "ll.mgz") ;

  if (penalize_zero_brain)
    fprintf(stderr, "%d zero brain voxels\n", nz) ;
  return(total_log_p / (double)num) ;
}
#else
double
GCAimageLogLikelihood(GCA *gca, MRI *mri_inputs, TRANSFORM *transform)
{
  int        x, y, z, width, height, depth, xn, yn, zn, n, label /*, found*/,
  xp, yp, zp ;
  GCA_NODE   *gcan ;
  GCA_PRIOR  *gcap ;
  GC1D       *gc ;
  double     total_log_p, log_p, min_log_p ;
  float      vals[MAX_GCA_INPUTS], min ;

  /* go through each voxel in the input volume and find the canonical
  voxel (and hence the classifier) to which it maps. Then update the
  classifiers statistics based on this voxel's intensity and label.
  */
  width = mri_inputs->width ;
  height = mri_inputs->height;
  depth = mri_inputs->depth ;
  min_log_p = 0 ;
  for (total_log_p = 0.0, x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;

        load_vals(mri_inputs, x, y, z, vals, gca->ninputs) ;

        /* find the node associated with this coordinate and classify */
        GCAsourceVoxelToNode(gca, mri_inputs,transform, x, y, z, &xn, &yn,&zn);
        if (xn < 0)
          xn = 0 ;
        else if (xn >= gca->node_width)
          xn = gca->node_width-1 ;
        if (yn < 0)
          yn = 0 ;
        else if (yn >= gca->node_height)
          yn = gca->node_height-1 ;
        if (zn < 0)
          zn = 0 ;
        else if (zn >= gca->node_depth)
          zn = gca->node_depth-1 ;

        gcan = &gca->nodes[xn][yn][zn] ;
        GCAsourceVoxelToPrior(gca, mri_inputs, transform, x, y, z,
                              &xp, &yp, &zp);
        if (xp < 0)
          xp = 0 ;
        else if (xp >= gca->prior_width)
          xp = gca->prior_width-1 ;
        if (yp < 0)
          yp = 0 ;
        else if (yp >= gca->prior_height)
          yp = gca->prior_height-1 ;
        if (zp < 0)
          zp = 0 ;
        else if (zp >= gca->prior_depth)
          zp = gca->prior_depth-1 ;
        gcap = &gca->priors[xp][yp][zp] ;
        if (gcap == NULL || gcan->nlabels == 1)
          label = gcan->labels[0] ;
        else
          label = gcaMaxPriorLabel(gca, mri_inputs, transform, x, y, z) ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          if (gcan->labels[n] == label)
            break ;
        }
        if (n < gcan->nlabels)
          gc = &gcan->gcs[n] ;
        else
          gc = findClosestValidGC(gca, xn, yn, zn, label, 0) ;

        log_p = gcaComputeLogDensity(gc, vals,gca->ninputs,
                                     getPrior(gcap, label),label) ;
        total_log_p += log_p ;
        if (log_p < min_log_p)
          min_log_p = log_p ;
        if (!check_finite("4", total_log_p))
        {
          DiagBreak() ;
          fprintf(stdout,
                  "total log p not finite at (%d, %d, %d)"
                  " n = %d,\n", x, y, z, n) ;
        }
      }
    }
  }
#if 0
  for (x = 0 ; x < width ; x++)
  {
    for (y = 0 ; y < height ; y++)
    {
      for (z = 0 ; z < depth ; z++)
      {
        if (x == Gx && y == Gy && z == Gz)
          DiagBreak() ;
        found = 1 ;
        if (!GCAsourceVoxelToNode(gca, mri_inputs,
                                  transform, x, y, z, &xn, &yn, &zn))
        {
          gcan = &gca->nodes[xn][yn][zn] ;
          gcap = getGCAP(gca, mri_inputs, transform, x, y, z) ;
          if (gcap==NULL)
            found = 0 ;
          else
          {
            label = gcaMaxPriorLabel(gca, mri_inputs, transform, x, y, z) ;
            for (n = 0 ; n < gcan->nlabels ; n++)
            {
              if (gcan->labels[n] == label)
                break ;
            }
            if (n >= gcan->nlabels)
              found = 0 ;
          }
        }
        else // outside fov
          found = 0 ;
        if (!found)
          total_log_p += min_log_p ; // penalize for non-found voxels
      }
    }
  }
#endif

  total_log_p /= (double)(mri_inputs->width*
                          mri_inputs->height*
                          mri_inputs->depth);
  return(total_log_p) ;
}
#endif

#if 0
static int
gcaMaxPriorLabel(GCA *gca, MRI *mri, TRANSFORM *transform, int x, int y, int z)
{
  int        n, max_label ;
  GCA_PRIOR  *gcap ;
  float      maxp ;

  gcap = getGCAP(gca, mri, transform, x, y, z) ;
  if (gcap->nlabels == 0)
    return(0) ;
  maxp = gcap->priors[0] ;
  max_label = gcap->labels[0] ;
  for (n = 1 ; n < gcap->nlabels ; n++)
  {
    if (gcap->priors[n] > maxp)
    {
      maxp = gcap->priors[n] ;
      max_label = gcap->labels[n] ;
    }
  }

  return(max_label) ;
}
#endif
#if 1
static int entropy_labels[] =
  {
    Left_Cerebral_White_Matter,
    Left_Cerebral_Cortex,
    Left_Cerebellum_White_Matter,
    Left_Cerebellum_Cortex,
    Left_Amygdala,
    Left_Hippocampus,
    Left_Thalamus_Proper,
    Left_Pallidum,
    Left_Caudate,
    Left_Putamen,
    Left_Lateral_Ventricle,
    Left_Inf_Lat_Vent,
    Left_VentralDC,
    Brain_Stem,
    Third_Ventricle,
    Fourth_Ventricle,
  } ;

static int contra_entropy_labels[] =
  {
    Right_Cerebral_White_Matter,
    Right_Cerebral_Cortex,
    Right_Cerebellum_White_Matter,
    Right_Cerebellum_Cortex,
    Right_Amygdala,
    Right_Hippocampus,
    Right_Thalamus_Proper,
    Right_Pallidum,
    Right_Caudate,
    Right_Putamen,
    Right_Lateral_Ventricle,
    Right_Inf_Lat_Vent,
    Right_VentralDC,
    Brain_Stem,
    Third_Ventricle,
    Fourth_Ventricle,
  } ;
#else
static int entropy_labels[] =
  {
    Right_Cerebral_White_Matter,
    Left_Cerebral_White_Matter,
    Left_Cerebral_Cortex,
    Right_Cerebral_Cortex,
    Left_Caudate,
    Right_Caudate
  } ;
#endif
#define NUM_ENTROPY_LABELS (sizeof(entropy_labels) / sizeof(entropy_labels[0]))
#define NUM_CONTRA_LABELS (sizeof(contra_entropy_labels) / sizeof(contra_entropy_labels[0]))

static int
compute_ll_scale_change(GCA *gca, 
                        MRI *mri, 
                        MRI *mri_aseg, 
                        TRANSFORM *transform,
                        int *labels, int *contra_labels,
                        float *scales, int nlabels, float step_size)
{
  float       dk[NUM_ENTROPY_LABELS] ;
  double      prior, plike, dist ;
  int         x, y, z, xn, yn, zn, l, num[NUM_ENTROPY_LABELS], ind ;
  GCA_PRIOR   *gcap ;
  GC1D        *gc ;
  float       vals[MAX_GCA_INPUTS] ;
  int         label_indices[MAX_CMA_LABEL+1], label ;

  memset(dk, 0, sizeof(dk)) ;
  memset(num, 0, sizeof(num)) ;

  for (l = 0 ; l <= MAX_CMA_LABEL ; l++)
    label_indices[l] = -1 ;
  for (l = 0 ; l < nlabels ; l++)
  {
    label_indices[labels[l]] = l ;
    label_indices[contra_labels[l]] = l ;
  }

  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() ;
        load_vals(mri, x, y, z, vals, gca->ninputs) ;
        label = (int)MRIgetVoxVal(mri_aseg, x, y, z, 0) ;
        gcap = getGCAP(gca, mri, transform, x, y, z) ;
        ind = label_indices[label] ;
        if (gcap == NULL || gcap->nlabels <= 1 || ind < 0)
          continue ;
        if (MRIlabelsInNbhd
            (mri_aseg, x, y, z, 1, label) < 26)  // avoid border voxels
          continue ;
        if (!GCAsourceVoxelToNode(gca, mri, transform,x, y, z, &xn, &yn, &zn))
        {
          gc = GCAfindGC(gca, xn, yn, zn, label) ;
          if (gc == NULL)
            continue ;
          plike = GCAcomputeConditionalDensity(gc, vals, gca->ninputs, label) ;
          prior = getPrior(gcap, label) ;

          dist = vals[0]-(gc->means[0]*scales[ind]) ;
          if (FZERO(dist))
            continue ;
          num[ind]++ ;
          dk[ind] += plike * prior * dist / (fabs(dist)) ;
          if (!finite(dk[l]))
            DiagBreak() ;
        }
      }
    }
  }
  for (l = 0 ; l < nlabels ; l++)
    if (num[l] > 0)
    {
      if (!finite(dk[l]))
        DiagBreak() ;
      dk[l] /= num[l] ;
      scales[l] += step_size * dk[l] ;
    }
  return(NO_ERROR) ;
}
int
GCArenormalizeWithEntropyMinimization(GCA *gca, 
                                      MRI *mri, 
                                      TRANSFORM *transform, 
                                      FILE *logfp)
{
  float     scales[NUM_ENTROPY_LABELS], ll, last_ll, peaks[NUM_ENTROPY_LABELS],
    contra_peaks[NUM_CONTRA_LABELS], pct_change, 
    last_scales[NUM_ENTROPY_LABELS] ;
  int       i, done = 0, peak_bin ;
  HISTOGRAM *h_gca ;
  MRI       *mri_aseg ;

  for (i = 0 ; i < NUM_ENTROPY_LABELS ; i++)
  {
    scales[i] = 1.0 ;
    h_gca = gcaGetLabelHistogram(gca, entropy_labels[i], 0) ;
    peak_bin = HISTOfindHighestPeakInRegion(h_gca, 0, h_gca->nbins) ;
    peaks[i] = h_gca->bins[peak_bin] ;
    HISTOfree(&h_gca) ;

    h_gca = gcaGetLabelHistogram(gca, contra_entropy_labels[i], 0) ;
    peak_bin = HISTOfindHighestPeakInRegion(h_gca, 0, h_gca->nbins) ;
    contra_peaks[i] = h_gca->bins[peak_bin] ;
    HISTOfree(&h_gca) ;
  }

  mri_aseg = GCAlabel(mri, gca, NULL, transform) ;
  if (Gdiag & DIAG_WRITE)
  {
    char fname[STRLEN] ;
    sprintf(fname, "seg%3.3d.mgz", 0) ;
    printf("writing current segmentation snapshot to %s\n", fname) ;
    MRIwrite(mri_aseg, fname) ;
  }
  last_ll = 
    ll = 
    GCAimagePosteriorLogProbability(gca, mri_aseg, mri, transform) ;
  printf("%3.3d: ll = %2.7f\n", 0, ll) ;
  i = 0 ;
  do
  {
    memmove(last_scales, scales, sizeof(scales)) ;
    compute_ll_scale_change(gca, mri, mri_aseg, 
                            transform, entropy_labels, 
                            contra_entropy_labels, 
                            scales, NUM_ENTROPY_LABELS,.1) ;
    gcaScale(gca, entropy_labels, contra_entropy_labels, 
             scales, NUM_ENTROPY_LABELS, 1);
    {
      int n ;
      for (n = 0 ; n < NUM_ENTROPY_LABELS ; n++)
        printf("scales[%s] = %2.3f, peak = %2.0f (rh=%2.0f)\n", 
               cma_label_to_name(entropy_labels[n]), scales[n],
               peaks[n]*scales[n], contra_peaks[n]*scales[n]) ;
    }
    ll = GCAimagePosteriorLogProbability(gca, mri_aseg, mri, transform) ;
    gcaScale(gca, entropy_labels, contra_entropy_labels, 
             scales, NUM_ENTROPY_LABELS, -1);

    pct_change = 100*(last_ll-ll) / last_ll ;
    if (pct_change < 0.01)
      done = 1 ;
    i++ ;
    printf("%3.3d: ll = %2.7f (%2.3f%%)\n", i, ll, pct_change) ;
    if (logfp)
      fprintf(logfp, "%3.3d: ll = %2.7f (%2.3f%%)\n", i, ll, pct_change) ;
    if (!((i+1)%1))
    {
      printf("recomputing MAP labels...\n") ;
      gcaScale(gca, entropy_labels, contra_entropy_labels, 
               scales, NUM_ENTROPY_LABELS, 1);
      GCAlabel(mri, gca, mri_aseg, transform) ;
      if (Gdiag & DIAG_WRITE && (!((i+1)%2)))
      {
        char fname[STRLEN] ;
        sprintf(fname, "seg%3.3d.mgz", i+1) ;
        printf("writing current segmentation snapshot to %s\n", fname) ;
        MRIwrite(mri_aseg, fname) ;
      }
      ll = GCAimagePosteriorLogProbability(gca, mri_aseg, mri, transform) ;
      gcaScale(gca, entropy_labels, contra_entropy_labels, 
               scales, NUM_ENTROPY_LABELS, -1);
    }
    if (last_ll < ll)
      memmove(scales, last_scales, sizeof(scales)) ;

    last_ll = ll ;
    if (i < 8)
      done = 0 ;
    if (logfp)
      fflush(logfp) ;
  }
  while (!done) ;

  for (i = 0 ; i < NUM_ENTROPY_LABELS ; i++)
  {
    printf("scaling %s by %2.3f from %2.0f to %2.0f (rh=%2.0f)\n", 
           cma_label_to_name(entropy_labels[i]), scales[i],
           peaks[i], peaks[i]*scales[i], contra_peaks[i]*scales[i]) ;
    if (logfp)
      fprintf(logfp, "scaling %s by %2.3f from %2.0f to %2.0f (rh=%2.0f)\n",
              cma_label_to_name(entropy_labels[i]), scales[i],
              peaks[i], peaks[i]*scales[i], contra_peaks[i]*scales[i]) ;
  }
  if (logfp)
    fflush(logfp) ;
  gcaScale(gca, entropy_labels, contra_entropy_labels, 
           scales, NUM_ENTROPY_LABELS, 1);
  return(NO_ERROR) ;
}

#if 0
static double
GCAcomputeScaledMeanEntropy(GCA *gca, 
                            MRI *mri, 
                            TRANSFORM *transform, 
                            int *labels, float *scales, int nlabels)
{
  double      entropy, entropy_total, p[MAX_LABELS_PER_GCAN], 
    ptotal, max_prior, max_like, p_like ;
  int         x, y, z, c, xn, yn, zn, num, max_prior_c, max_like_c, l, i ;
  GCA_PRIOR   *gcap ;
  GC1D        *gc ;
  float      vals[MAX_GCA_INPUTS] ;
  int         label_indices[MAX_CMA_LABEL+1] ;

  for (l = 0 ; l <= MAX_CMA_LABEL ; l++)
    label_indices[l] = -1 ;
  for (l = 0 ; l < nlabels ; l++)
    label_indices[labels[l]] = l ;

  for (num = 0, entropy_total = 0.0, 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() ;
        load_vals(mri, x, y, z, vals, gca->ninputs) ;
        if (vals[0] < 40)
          continue ;  // skull stripped region
        gcap = getGCAP(gca, mri, transform, x, y, z) ;
        if (gcap == NULL || gcap->nlabels <= 1)
          continue ;
        if (!GCAsourceVoxelToNode(gca, mri, transform,x, y, z, &xn, &yn, &zn))
        {
          max_prior = max_like = 0 ;
          max_prior_c = max_like_c = 0 ;
          for (ptotal = 0, c = 0 ; c < gcap->nlabels ; c++)
          {
            gc = GCAfindGC(gca, xn, yn, zn, gcap->labels[c]) ;
            if (gc == NULL)
            {
              p[c] = 0 ;
              continue ;
            }
            if (label_indices[gcap->labels[c]] >= 0)  // scale up
            {
              for (i = 0 ; i < gca->ninputs ; i++)
                gc->means[i] += scales[label_indices[gcap->labels[c]]] ;
            }
            p_like = GCAcomputeConditionalDensity(gc, 
                                                  vals, 
                                                  gca->ninputs, 
                                                  gcap->labels[c]) ;
            p[c] = gcap->priors[c]*p_like ;
            if (label_indices[gcap->labels[c]] >= 0)  // scale back down
            {
              for (i = 0 ; i < gca->ninputs ; i++)
                gc->means[i] -= scales[label_indices[gcap->labels[c]]] ;
            }
            ptotal += p[c] ;
            if (gcap->priors[c] > max_prior)
            {
              max_prior = gcap->priors[c] ;
              max_prior_c = c ;
            }
            if (p_like > max_like)
            {
              max_like = p_like ;
              max_like_c = c ;
            }
          }
          if (FZERO(ptotal))
            continue ;
          for (entropy = 0.0, c = 0 ; c < gcap->nlabels ; c++)
          {
            p[c] /= ptotal ;
            if (DZERO(p[c]))
              continue ;
            //            if (c == max_prior_c && c == max_like_c)
            {
              if (label_indices[gcap->labels[c]] >= 0)
                DiagBreak() ;
              entropy += p[c] * log(p[c]) ;
            }
          }
          num++ ;
          if (!finite(entropy))
            DiagBreak() ;
          entropy_total += entropy ;
        }
      }
    }
  }

  return(-entropy_total/num) ;
}
#endif
double
GCAcomputeMeanEntropy(GCA *gca, MRI *mri, TRANSFORM *transform)
{
  double      entropy, entropy_total, p[MAX_LABELS_PER_GCAN], ptotal, max_p ;
  int         x, y, z, c, xn, yn, zn, num, max_c ;
  GCA_PRIOR   *gcap ;
  GC1D        *gc ;
  float      vals[MAX_GCA_INPUTS] ;

  num = 0 ;
  for (entropy_total = 0.0, 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() ;
        gcap = getGCAP(gca, mri, transform, x, y, z) ;
        if (gcap == NULL || gcap->nlabels <= 1)
          continue ;
        if (!GCAsourceVoxelToNode(gca, mri, transform,x, y, z, &xn, &yn, &zn))
        {
          load_vals(mri, x, y, z, vals, gca->ninputs) ;
          max_p = 0 ;
          max_c = 0 ;
          for (ptotal = 0, c = 0 ; c < gcap->nlabels ; c++)
          {
            gc = GCAfindGC(gca, xn, yn, zn, gcap->labels[c]) ;
            if (gc == NULL)
            {
              p[c] = 0 ;
              continue ;
            }
            p[c] = GCAcomputeConditionalDensity(gc, 
                                                vals, 
                                                gca->ninputs, 
                                                gcap->labels[c]) ;
            ptotal += p[c] ;
            if (p[c] > max_p)
            {
              max_p = p[c] ;
              max_c = c ;
            }
          }
          if (FZERO(ptotal))
            continue ;
          for (entropy = 0.0, c = 0 ; c < gcap->nlabels ; c++)
          {
            p[c] /= ptotal ;
            if (DZERO(p[c]))
              continue ;
            entropy += p[c] * log(p[c]) ;
          }
          num++ ;
          if (!finite(entropy))
            DiagBreak() ;
          entropy_total += entropy ;
        }
      }
    }
  }

  return(-entropy_total/num) ;
}
static int
gcaScale(GCA *gca, 
         int *labels, 
         int *contra_labels, 
         float *scales, 
         int nlabels, 
         int dir)
{
  int         label_indices[MAX_CMA_LABEL+1], x, y, z, n, label, ind, i ;
  GC1D        *gc ;
  GCA_NODE    *gcan ;
  float       scale ;


  for (i = 0 ; i <= MAX_CMA_LABEL ; i++)
    label_indices[i] = -1 ;
  for (i = 0 ; i < nlabels ; i++)
  {
    label_indices[labels[i]] = i ;
    label_indices[contra_labels[i]] = i ;
  }

  for (x = 0 ; x < gca->node_width ; x++)
  {
    for (y = 0 ; y < gca->node_height ; y++)
    {
      for (z = 0 ; z < gca->node_depth ; z++)
      {
        gcan = &gca->nodes[x][y][z] ;
        for (n = 0 ; n < gcan->nlabels ; n++)
        {
          label = gcan->labels[n] ;
          ind = label_indices[label] ;
          if (ind < 0)
            continue ;
          scale = scales[ind] ;
          gc = &gcan->gcs[n] ;
          for (i = 0 ; i < gca->ninputs ; i++)
          {
            if (dir > 0)
              gc->means[i] *= scale ;
            else
              gc->means[i] /= scale ;
          }
        }
      }
    }
  }
  return(NO_ERROR) ;
}

static int
gcapGetMaxPriorLabel(GCA_PRIOR *gcap, double *p_prior)
{
  int n, best_label = 0 ;
  float max_p ;

  *p_prior = 0 ;
  if (gcap == NULL)
    return(0) ;

  max_p = -1 ;
  for (n = 0 ; n < gcap->nlabels ; n++)
  {
    if (gcap->priors[n] > max_p)
    {
      max_p = gcap->priors[n] ;
      best_label = gcap->labels[n] ;
    }
  }
  *p_prior = max_p ;
  return(best_label) ;
}

#if 0
static int
gcapBrainIsPossible(GCA_PRIOR *gcap)
{
  int possible, n ;

  for (n = possible = 0 ; n < gcap->nlabels ; n++)
    if (IS_BRAIN(gcap->labels[n]))
    {
      possible = 1 ;
      break ;
    }
  return(possible) ;
}
#endif
float
GCAcomputeLabelPosterior(GCA *gca, TRANSFORM *transform, MRI *mri, float x, float y, float z, int label)
{
  float    p, plabel, vals[MAX_GCA_INPUTS], ptotal ;
  GCA_PRIOR *gcap ;
  int       n, olabel ;

  load_vals(mri, x, y, z, vals, gca->ninputs) ;
  gcap = getGCAPfloat(gca, mri, transform, x, y, z) ;
  if (gcap == NULL)
    return(0.0) ;

  if (gcap->total_training > 0)
    plabel = .01 / gcap->total_training ;
  else
    plabel = 0.0 ;
  for (ptotal = 0.0, n = 0 ; n < gcap->nlabels ; n++)
  {
    olabel = gcap->labels[n] ;
    p = 
      GCAlabelProbability(mri, gca, transform, x, y, z, olabel)
      * gcap->priors[n];
    if (olabel == label)
      plabel = p ;

    ptotal += p ;
  }

  if (!FZERO(ptotal))
    plabel /= ptotal ;
  return(plabel) ;
}

float
GCAcomputeLabelLikelihood(GCA *gca, TRANSFORM *transform, MRI *mri, float x, float y, float z, int label)
{
  float    p, plabel, vals[MAX_GCA_INPUTS], ptotal ;
  GCA_PRIOR *gcap ;
  int       n, olabel, found = 0 ;

  gcap = getGCAPfloat(gca, mri, transform, x, y, z) ;
  if (gcap == NULL)
    return(0.0) ;

  if (gcap->total_training > 0)
    plabel = .01 / gcap->total_training ;
  else
    plabel = 0.0 ;
  for (ptotal = 0.0, n = 0 ; n < gcap->nlabels ; n++)
  {
    olabel = gcap->labels[n] ;
    p =  GCAlabelProbability(mri, gca, transform, x, y, z, olabel);
    if (olabel == label)
    {
      found = 1 ;
      plabel = p ;
    }

    ptotal += p ;
  }

  if (found == 0)
  {
    GC1D *gc ;
    int  xn, yn, zn ;

    if (!GCAsourceVoxelToNode(gca, mri, transform, x, y, z, &xn, &yn, &zn))
    {
      gc = findClosestValidGC(gca, xn, yn, zn, label, 0) ;
      if (gc == NULL)
        DiagBreak() ;
      else
      {
        load_vals(mri, x, y, z, vals, gca->ninputs) ;
        plabel = GCAcomputeConditionalDensity(gc, vals, gca->ninputs, label) ;
        ptotal += plabel ;
      }
    }
  }

  if (!FZERO(ptotal))
    plabel /= ptotal ;
  return(plabel) ;
}

