Skip to content
Snippets Groups Projects
ccops.cc 22.77 KiB
/*  Copyright (C) 2004 University of Oxford  */

/*  CCOPYRIGHT  */

#include <iostream>
#include <fstream>
#include <cmath>
#include "newimage/newimageall.h"
#include "ccopsOptions.h"
#include <vector>
#include <algorithm>

using namespace std;
using namespace NEWIMAGE;
using namespace NEWMAT;
using namespace CCOPS;
  void spect_reord(SymmetricMatrix& A,ColumnVector& r,ColumnVector& y){
    SymmetricMatrix Q=-A;
      DiagonalMatrix t(Q.Nrows());
      t=0;
      DiagonalMatrix D;
      Matrix V;
      for(int i=1;i<=Q.Nrows();i++){
        float rowsum1=0, rowsum2=0;
        for(int j=1;j<=Q.Ncols();j++){
          if(i!=j) rowsum1+=Q(i,j);
          rowsum2+=A(i,j);
        }
          Q(i,i)=-rowsum1;
          t(i)=1/sqrt(rowsum2);
      }
      Q << t*Q*t;
      EigenValues(Q,D,V);
      vector<pair<float,int> > myvec;
      vector<pair<float,int> > myvec2;
      
      for(int i=1;i<=D.Nrows();i++){
	pair<float,int> mypair;
	mypair.first=D(i);
	mypair.second=i;
	myvec.push_back(mypair);
      }
      sort(myvec.begin(),myvec.end());
      int ind=myvec[1].second; // index for second eigenval
      
      ColumnVector v2scale(V.Nrows());
      for(int i=1;i<=V.Nrows();i++){
	v2scale(i)=V(i,ind); //second eigvec
      }
      v2scale=t*v2scale; //scale it
      
      
      for(int i=1;i<=D.Nrows();i++){
	pair<float,int> mypair;
	mypair.first=v2scale(i);
	mypair.second=i;
	myvec2.push_back(mypair);
      }
      //myvec2 contains scaled second eigenvector and index for sorting.
      
      
      sort(myvec2.begin(),myvec2.end());
      r.ReSize(D.Nrows());
      y.ReSize(D.Nrows());
      
      for(int i=1;i<=D.Nrows();i++){
	y(i)=myvec2[i-1].first;
	r(i)=myvec2[i-1].second;
      }
      
  } 

bool compare(const pair<float,int> &r1,const pair<float,int> &r2){
  return (r1.first<r2.first);
}
void randomise(vector< pair<float,int> >& r){
  for(unsigned int i=1;i<=r.size();i++){
    pair<float,int> p(rand()/float(RAND_MAX),i);
    r[i-1]=p;
  }
  sort(r.begin(),r.end(),compare);
  
}
void do_kmeans(const Matrix& data,ColumnVector& y,const int k){
  int numiter=200;
  if(data.Nrows() != (int)y.Nrows()){
    y.ReSize(data.Nrows());
  }
  int n = data.Nrows();
  int d = data.Ncols();
  Matrix means(d,k),newmeans(d,k);
  ColumnVector nmeans(k);
  means=0;
  nmeans=0;
  
  // // initialise random
//   vector< pair<float,int> > rindex(n);
//   randomise(rindex);
//   vector<pair<float,int> >::iterator riter;
//   int nn=0,cl=1,nperclass=(int)(float(n)/float(k));
//   for(riter=rindex.begin();riter!=rindex.end();++riter){
//     means.Column(cl) += data.Row((*riter).second).t();
//     nmeans(cl) += 1;
//     y((*riter).second)=cl;
//     nn++;
//     if(nn>=nperclass && cl<k){
//       nn=0;
//       cl++;
//     }
//   }
//   for(int m=1;m<=k;m++)
//     means.Column(m) /= nmeans(m);


// initialise with far-away trick
// start with a random class centre. then each new class centre is
  // as far as possible from the cog of the previous classes
  means.Column(1) = data.Row(round(rand()/float(RAND_MAX)*float(n-1))+1).t();
  ColumnVector cog(d);
  for(int cl=2;cl<=k;cl++){
    cog = sum(means.SubMatrix(1,d,1,cl-1),2);
    
    int maxi=1;float dist=0,maxdist=0;
    for(int i=1;i<=n;i++){
      float cdist=0,mindist=-1;int minc=1;
      for(int prevcl=cl-1;prevcl>=1;prevcl--){
	cdist = (means.Column(prevcl)-data.Row(i).t()).SumSquare();
	if(mindist==-1 || cdist<mindist){mindist=cdist;minc=prevcl;}
      }
      dist = mindist;
      if(dist>=maxdist){maxdist=dist;maxi=i;}
    }
    means.Column(cl)=data.Row(maxi).t();
  }


  
  // iterate
  for(int iter=0;iter<numiter;iter++){
    // loop over datapoints and attribute z for closest mean
    newmeans=0;
    nmeans=0;
    for(int i=1;i<=n;i++){
      float mindist=1E20,dist=0;
      int mm=1;
      for(int m=1;m<=k;m++){
	dist = (means.Column(m)-data.Row(i).t()).SumSquare();
	if( dist<mindist){
	  mindist=dist;
	  mm = m;
	}
      }
      y(i) = mm;
      newmeans.Column(mm) += data.Row(i).t();
      nmeans(mm) += 1;
    }
    
    // compute means
    for(int m=1;m<=k;m++){
      if(nmeans(m)==0){
	cout << "Only found " << k-1 << " clusters!!!" << endl;
	do_kmeans(data,y,k-1);
	return;
      }
      newmeans.Column(m) /= nmeans(m);
    }
    means = newmeans;
  }
  
}




void kmeans_reord(const Matrix& A,ColumnVector& r,ColumnVector& y,const int k){
  do_kmeans(A,y,k);
 
  vector< pair<float,int> > myvec2;
  for(int i=1;i<=A.Nrows();i++){
    pair<int,int> mypair;
    mypair.first=y(i);
    mypair.second=i;
    myvec2.push_back(mypair);
  }  
  
  sort(myvec2.begin(),myvec2.end());
  r.ReSize(A.Nrows());
  y.ReSize(A.Nrows());
  
  for(int i=1;i<=A.Nrows();i++){
    y(i)=myvec2[i-1].first;
    r(i)=myvec2[i-1].second;
  } 
}

void do_fuzzy(const Matrix& data,Matrix& u,const int k){
  int numiter     = 200;
  float fuzziness = 2;

  int n = data.Nrows();
  int d = data.Ncols();
  Matrix means(d,k),newmeans(d,k);
  ColumnVector nmeans(k);
  means=0;
  nmeans=0;
  
  // initialise with far-away trick
  // start with a random class centre. then each new class centre is
  // as far as possible from the cog of the previous classes
  means.Column(1) = data.Row(round(rand()/float(RAND_MAX)*float(n-1))+1).t();
  ColumnVector cog(d);
  for(int cl=2;cl<=k;cl++){
    cog = sum(means.SubMatrix(1,d,1,cl-1),2);    
    int maxi=1;float dist=0,maxdist=0;
    for(int i=1;i<=n;i++){
      float cdist=0,mindist=-1;int minc=1;
      for(int prevcl=cl-1;prevcl>=1;prevcl--){
	cdist = (means.Column(prevcl)-data.Row(i).t()).SumSquare();
	if(mindist==-1 || cdist<mindist){mindist=cdist;minc=prevcl;}
      }
      dist = mindist;
      if(dist>=maxdist){maxdist=dist;maxi=i;}
    }
    means.Column(cl)=data.Row(maxi).t();
  }

  // iterate
  for(int iter=0;iter<numiter;iter++){
    // loop over datapoints and attribute z for closest mean
    newmeans=0.0;
    nmeans=0;
    for(int i=1;i<=n;i++){
      float mindist=-1,dist=0;
      int mm=1;
      for(int m=1;m<=k;m++){
	dist = (means.Column(m)-data.Row(i).t()).SumSquare();
	if( mindist==-1 || dist<mindist){
	  mindist=dist;
	  mm = m;
	}
      }
      //y(i) = mm;
      newmeans.Column(mm) += data.Row(i).t();
      nmeans(mm) += 1;
    }
    
    // compute means
    for(int m=1;m<=k;m++){
      if(nmeans(m)!=0)
	newmeans.Column(m) /= nmeans(m);
      means.Column(m) = newmeans.Column(m);
    }
  }

  // now use this to calculate u
  u.ReSize(n,k);
  u=0.0;
  for(int i=1;i<=n;i++){
    for(int j=1;j<=k;j++){
      float xi_cj = (data.Row(i) - means.Column(j).t()).SumSquare();
      if(xi_cj==0){u.Row(i)=0.01/float(k-1);u(i,j)=0.99;break;}
      float xi_cl;
      for(int l=1;l<=k;l++){
	xi_cl = (data.Row(i) - means.Column(l).t()).SumSquare();
	u(i,j) += std::exp(std::log(xi_cj/xi_cl)/(fuzziness-1));
      }
      u(i,j) = 1/u(i,j);
    }
  }
  

}


void fuzzy_reord(const Matrix& A,Matrix& u,ColumnVector& r,ColumnVector& y,const int k){
  do_fuzzy(A,u,k);
  //OUT(u);

  float junk;
  int index;
  y.ReSize(A.Nrows());
  r.ReSize(A.Nrows());
  for(int i=1;i<=A.Nrows();i++){
    junk = u.Row(i).Maximum1(index);
    y(i) = index;
  }
 
  vector< pair<float,int> > myvec2;
  for(int i=1;i<=A.Nrows();i++){
    pair<int,int> mypair;
    mypair.first=y(i);
    mypair.second=i;
    myvec2.push_back(mypair);
  }  
  
  sort(myvec2.begin(),myvec2.end());
  r.ReSize(A.Nrows());
  y.ReSize(A.Nrows());
  
  
  for(int i=1;i<=A.Nrows();i++){
    y(i)=myvec2[i-1].first;
    r(i)=myvec2[i-1].second;
  } 
  
}



void rem_zrowcol(const Matrix& myOMmat,const Matrix& coordmat,const Matrix& tractcoordmat,const bool coordbool,const bool tractcoordbool,Matrix& newOMmat,Matrix& newcoordmat, Matrix& newtractcoordmat)
{
 
 vector<int> zerorows;
 vector<int> zerocols;
 int dimsum =0;



 cout<< "Checking for all zero rows"<<endl;
 for(int i=1;i<=myOMmat.Nrows();i++){
   dimsum=0;
   for(int j=1;j<=myOMmat.Ncols();j++){
     dimsum+=(int)myOMmat(i,j);
   }
   if(dimsum==0){zerorows.push_back(i);}
 }



 cout<< "Checking for all zero cols"<<endl;
 for(int j=1;j<=myOMmat.Ncols();j++){
   dimsum=0;
   for(int i=1;i<=myOMmat.Nrows();i++){
     dimsum+=(int)myOMmat(i,j);
   }
   if(dimsum==0){zerocols.push_back(j);}
 }


 newOMmat.ReSize(myOMmat.Nrows()-zerorows.size(),myOMmat.Ncols()-zerocols.size());
 if(coordbool){
   newcoordmat.ReSize(coordmat.Nrows()-zerorows.size(),3);
 }
 if(tractcoordbool){
   newtractcoordmat.ReSize(tractcoordmat.Nrows()-zerocols.size(),3);
 }

 int zrowcounter=0,zcolcounter=0,nzrowcounter=1,nzcolcounter=1;
 cout<<"Forming New Matrix"<<endl;
 for(int j=1;j<=myOMmat.Ncols();j++){
   zrowcounter=0;
   nzrowcounter=1;

   if(zerocols.size()>0){ //Are there any Zero Columns
     if(zerocols[zcolcounter]!=j){  // Only add a col if it's not the next zcol
       for(int i=1;i<=myOMmat.Nrows();i++){
	 if(zerorows.size()>0){ //Are there any zero rows?
	   if(zerorows[zrowcounter]!=i){ //Only add a row if it's not the next zrow
	     newOMmat(nzrowcounter,nzcolcounter)=myOMmat(i,j);
	     nzrowcounter++;
	   }
	   else{zrowcounter++;}
	 }
	 else{newOMmat(nzrowcounter,nzcolcounter)=myOMmat(i,j);nzrowcounter++;}//No Zero Rows
       }
       nzcolcounter++;
     }
     else{zcolcounter++;} //move onto next z col
   }
   
   else{  //No zero Columns
     for(int i=1;i<=myOMmat.Nrows();i++){
       if(zerorows.size()>0){
	 if(zerorows[zrowcounter]!=i){ //Only add a row if it's not the next zrow
	   newOMmat(nzrowcounter,nzcolcounter)=myOMmat(i,j);
	   nzrowcounter++;
	 }
	 else{zrowcounter++;}
       }
       else{newOMmat(nzrowcounter,nzcolcounter)=myOMmat(i,j);nzrowcounter++;}
     }
     nzcolcounter++;
   }

}


 if(coordbool){
   cout<<"Updating Seed Coordinates"<<endl;
   zrowcounter=0;nzrowcounter=1;
   if(zerorows.size()>0){//Are there any zero rows?
     for(int i=1;i<=coordmat.Nrows();i++){
       if(zerorows[zrowcounter]!=i){
	 newcoordmat(nzrowcounter,1)=coordmat(i,1);
	 newcoordmat(nzrowcounter,2)=coordmat(i,2);
	 newcoordmat(nzrowcounter,3)=coordmat(i,3);
	 nzrowcounter++;
       }
       else{zrowcounter++;}
     }
   } 
   else{//No zero Rows
     newcoordmat=coordmat;
   }
}

 if(tractcoordbool){
   cout<<"Updating Tract Coordinates"<<endl;
   zcolcounter=0;nzcolcounter=1;
   if(zerocols.size()>0){//Are there any zero cols?
     for(int i=1;i<=tractcoordmat.Nrows();i++){
       if(zerocols[zcolcounter]!=i){
	 newtractcoordmat(nzcolcounter,1)=tractcoordmat(i,1);
	 newtractcoordmat(nzcolcounter,2)=tractcoordmat(i,2);
	 newtractcoordmat(nzcolcounter,3)=tractcoordmat(i,3);
	 nzcolcounter++;
       }
       else{zcolcounter++;}
     }
   }
   else{//No zero Cols 
     newtractcoordmat=tractcoordmat;
   }
 }

}









void rem_cols(Matrix& myOMmat,Matrix& tractcoordmat,const bool tractcoordbool,const vector<int>& excl_cols)
{
 

 Matrix newOMmat,newtractcoordmat;
 newOMmat.ReSize(myOMmat.Nrows(),myOMmat.Ncols()-excl_cols.size());

 if(tractcoordbool){
   newtractcoordmat.ReSize(tractcoordmat.Nrows()-excl_cols.size(),3);
 }

 int zrowcounter=0,zcolcounter=0,nzcolcounter=1,nzrowcounter=1;
 vector<int> zerorows;

 for(int j=1;j<=myOMmat.Ncols();j++){
   zrowcounter=0;
   nzrowcounter=1;   

   if(excl_cols.size()>0){ //Are there any excl Columns
     if(excl_cols[zcolcounter]!=j){  // Only add a col if it's not the next zcol
       for(int i=1;i<=myOMmat.Nrows();i++){
	 if(zerorows.size()>0){ //Are there any excl rows?
	   if(zerorows[zrowcounter]!=i){ //Only add a row if it's not the next zrow
	     newOMmat(nzrowcounter,nzcolcounter)=myOMmat(i,j);
	     nzrowcounter++;
	   }
	   else{zrowcounter++;}
	 }
	 else{newOMmat(nzrowcounter,nzcolcounter)=myOMmat(i,j);nzrowcounter++;}//No Zero Rows
       }
       nzcolcounter++;
     }
     else{zcolcounter++;} //move onto next z col
   }
   else{  //No zero Columns
     for(int i=1;i<=myOMmat.Nrows();i++){
       if(zerorows.size()>0){
	 if(zerorows[zrowcounter]!=i){ //Only add a row if it's not the next zrow
	   newOMmat(nzrowcounter,nzcolcounter)=myOMmat(i,j);
	   nzrowcounter++;
	 }
	 else{zrowcounter++;}
       }
       else{newOMmat(nzrowcounter,nzcolcounter)=myOMmat(i,j);nzrowcounter++;}
     }
     nzcolcounter++;
   }
}

 
 if(tractcoordbool){
   zcolcounter=0;nzcolcounter=1;
   if(excl_cols.size()>0){//Are there any zero cols?
     for(int i=1;i<=tractcoordmat.Nrows();i++){
       if(excl_cols[zcolcounter]!=i){
	 newtractcoordmat(nzcolcounter,1)=tractcoordmat(i,1);
	 newtractcoordmat(nzcolcounter,2)=tractcoordmat(i,2);
	 newtractcoordmat(nzcolcounter,3)=tractcoordmat(i,3);
	 nzcolcounter++;
       }
       else{zcolcounter++;}
     }
   }
   else{//No zero Cols 
     newtractcoordmat=tractcoordmat;
   }
 }

 myOMmat = newOMmat;
 tractcoordmat = newtractcoordmat;
  
}

void add_connexity(SymmetricMatrix& CtCt,const Matrix& coord,const float p=.5){
  // compute CtCt range
  float r=CtCt.Minimum();
  float R=CtCt.Maximum();

  // compute distance matrix
  SymmetricMatrix D(coord.Nrows());
  for(int i=1;i<=coord.Nrows();i++)
    for(int j=1;j<=i;j++){
      D(i,j)=std::sqrt((coord.Row(i)-coord.Row(j)).SumSquare());
    }
  D=D.MaximumAbsoluteValue()-D;

  // change distance range
  float m=D.Minimum(),M=D.Maximum();
  D=(D-m)*(R-r)/(M-m)+r;

  // add distance to CtCt matrix
  for(int i=1;i<=coord.Nrows();i++)
    for(int j=1;j<=i;j++){
      CtCt(i,j)=std::sqrt((1-p)*CtCt(i,j)*CtCt(i,j)+p*D(i,j)*D(i,j));
    }
 
}



int main ( int argc, char **argv ){
  ccopsOptions& opts = ccopsOptions::getInstance();
  int success=opts.parse_command_line(argc,argv);
  if(!success) return 1;
  if(opts.inmatrix.value()=="" && opts.directory.value()==""){
    cerr << "Specify either input matrix or tractography directory" << endl;
    return 1;
  }
  string ip=opts.inmatrix.value();
  make_basename(ip);
  
  ColumnVector y1,r1,y2,r2;
  Matrix U;
  volume<int> myOM;
  volume<int> coordvol;
  volume<int> tractcoordvol;
  bool coordbool=false,tractcoordbool=false;
  //  read_volume(myOM,ip);
  read_volume(myOM,opts.directory.value()+"/"+opts.inmatrix.value());

  Matrix myOMmat(myOM.xsize(),myOM.ysize());
  Matrix mycoordmat,mytractcoordmat;
  Matrix newOMmat,newcoordmat,newtractcoordmat;
  
  for(int j=0;j<myOM.ysize();j++){
    for(int i=0;i<myOM.xsize();i++){
      if(opts.bin.value()==0)
	myOMmat(i+1,j+1)=float(myOM(i,j,0)); 
      else{
	if(myOM(i,j,0)>opts.bin.value()){
	  myOMmat(i+1,j+1)=1.0f;
	}
	else{
	  myOMmat(i+1,j+1)=0.0f;
	}
      }
    }
  }
  // write_ascii_matrix(newOMmat.t(),"preprecock");
  //Checking for and loading up Seed Coordinates

  string coordname=opts.directory.value()+"/coords_for_"+ip;
  if(fsl_imageexists(coordname)){
    read_volume(coordvol,coordname);
    coordbool=true;
    mycoordmat.ReSize(coordvol.xsize(),coordvol.ysize());
    for(int j=0;j<coordvol.ysize();j++){
      for(int i=0;i<coordvol.xsize();i++){
	mycoordmat(i+1,j+1)=coordvol(i,j,0);
      }
    }
  }
  else{
    cout<<"Seed Space Coordinate File Not present - Ignoring"<<endl;
  }

  //Checking For and Loading Up Tract coordinates
  string trcoordname=opts.directory.value()+"/tract_space_coords_for_"+ip;
  if(fsl_imageexists(trcoordname)){
    read_volume(tractcoordvol,trcoordname);
    tractcoordbool=true;
    mytractcoordmat.ReSize(tractcoordvol.xsize(),tractcoordvol.ysize());
    for(int j=0;j<tractcoordvol.ysize();j++){
      for(int i=0;i<tractcoordvol.xsize();i++){
	mytractcoordmat(i+1,j+1)=tractcoordvol(i,j,0);
      }
    }
  }
  else{
    cout<<"Tract Space Coordinate File Not present - Ignoring"<<endl;
  }
 


  // If user specifies an exclusion mask in tract space. 
  // work out which columns in the matrix to remove 
  // This only works if there is a lookup matrix available


  if(opts.excl_mask.value()!=""){
    volume<int> lookup_tract;
    volume<int> excl;
    read_volume(lookup_tract,opts.directory.value()+"/lookup_tractspace_"+ip);
    string exname=opts.excl_mask.value();
    make_basename(exname);
    read_volume(excl,exname);
    if(!samesize(excl,lookup_tract)){
      cerr<<"Whoops - your exlusion mask does not appear to be in the same space as your original low resolution mask - sorry"<<endl;
      return(-1);
    }
    vector<int> excl_cols;
    for(int k=0;k<=excl.zsize();k++){
      for(int j=0;j<=excl.ysize();j++){
	for(int i=0;i<=excl.xsize();i++){
	  if(excl(i,j,k)==1){
	    if(lookup_tract(i,j,k)!=0){
	     
	      if(lookup_tract(i,j,k)<=mytractcoordmat.Nrows()){
		excl_cols.push_back(lookup_tract(i,j,k)+1);
	      }
	      else{
		cerr<<"Something a bit dodgy has happened here"<<endl;
		cerr<<"Have you already run a reord_OM on this matrix"<<endl;
		cerr<<"If so you can't use an exclusion mask as the"<<endl;
		cerr<<"tractspace_lookup volume is not valid for this matrix"<<endl;
	      }
	     
	     
	    }
	  }
	}
      }
    }

    rem_cols(myOMmat,mytractcoordmat,tractcoordbool,excl_cols);
  }

 
  rem_zrowcol(myOMmat,mycoordmat,mytractcoordmat,coordbool,tractcoordbool,newOMmat,newcoordmat,newtractcoordmat);
  //   cerr<<"NOW"<<endl;
  //   cerr<<myOMmat.MaximumAbsoluteValue()<<endl;
  //   cerr<<newOMmat.MaximumAbsoluteValue()<<endl;
 
  //write_ascii_matrix("ncm",newcoordmat);
  // write_ascii_matrix("nctm",newtractcoordmat);



  string base=opts.basename.value();
  make_basename(base);
  volume<float> outCCvol(newOMmat.Nrows(),newOMmat.Nrows(),1);
  volume<int> outcoords(newcoordmat.Nrows(),3,1);



  cout<<"Computing correlation"<<endl;
  SymmetricMatrix CtCt;
  CtCt << corrcoef(newOMmat.t());
  CtCt << CtCt+1;

  // adding connexity constraint
  if(!coordbool){
    cout<<"WARNING !! No coordinates provided. I cannot apply any connexity constraint."<<endl;
  }
  else{
    add_connexity(CtCt,newcoordmat,opts.connexity.value());
  }

  if(opts.power.value()!=1){
    CtCt << pow(CtCt,opts.power.value());
  }

 
  if(!opts.reord1.value()){
     
    for(int j=0;j<outCCvol.ysize();j++){
      for(int i=0;i<outCCvol.xsize();i++){
	outCCvol(i,j,0)=CtCt(i+1,j+1);
      }
    }
   
   
    if(coordbool){
      for(int i=0;i<outcoords.xsize();i++){
	outcoords(i,0,0)=(int)newcoordmat(i+1,1);
	outcoords(i,1,0)=(int)newcoordmat(i+1,2);
	outcoords(i,2,0)=(int)newcoordmat(i+1,3);
      } 
    }
   
    save_volume(outCCvol,opts.directory.value()+"/CC_"+base);
    save_volume(outcoords,opts.directory.value()+"/coords_for_"+base);

  }
  else{
    cout<<"Starting First Reordering"<<endl;

    if(opts.scheme.value()=="spectral")
      spect_reord(CtCt,r1,y1);
    else if(opts.scheme.value()=="kmeans")
      kmeans_reord(CtCt,r1,y1,opts.nclusters.value());
    else if(opts.scheme.value()=="fuzzy")
      fuzzy_reord(CtCt,U,r1,y1,opts.nclusters.value());
    else{
      cerr << "unkown reordering scheme" << endl;
      return(-1);
    }
   
    cout<<"Permuting seed CC matrix"<<endl;
    for(int j=0;j<outCCvol.ysize();j++){
      for(int i=0;i<outCCvol.xsize();i++){
	outCCvol(i,j,0)=CtCt((int)r1(i+1),(int)r1(j+1));
      }
    }
   
   
    if(coordbool){
      cout<<"Permuting Seed Coordinates"<<endl;
//       OUT(r1.Maximum());
//       OUT(r1.Minimum());
//       OUT(newcoordmat.Ncols());
//       OUT(newcoordmat.Nrows());

      for(int i=0;i<outcoords.xsize();i++){
	outcoords(i,0,0)=(int)newcoordmat(int(r1(i+1)),1);
	outcoords(i,1,0)=(int)newcoordmat(int(r1(i+1)),2);
	outcoords(i,2,0)=(int)newcoordmat(int(r1(i+1)),3);
      } 
    }
    cout<<"Saving results"<<endl;
    write_ascii_matrix(r1,opts.directory.value()+"/"+base+"r1");
    write_ascii_matrix(y1,opts.directory.value()+"/"+base+"y1");
    save_volume(outCCvol,opts.directory.value()+"/reord_CC_"+base);
    save_volume(outcoords,opts.directory.value()+"/coords_for_reord_"+base);

    // propagate seed clustering onto tract space if requested
    if(opts.reord3.value()){
      if(opts.scheme.value() == "spectral"){
	cerr << "Warning: cannot propagate to tract space under this scheme." << endl;
	cerr << "will carry on ignoring this option"<<endl;
      }
      else{
	cout << "Propagating seed clustering into tract space" << endl;
	volume<float> opaths;
	read_volume(opaths,opts.directory.value()+"/lookup_tractspace_"+ip);
	opaths = 0.0;

	// firstly, determine sum of matrix2 over different classes
	Matrix sumOverSeeds(y1.Maximum(),newtractcoordmat.Nrows());
	sumOverSeeds = 0.0;
	for(int j=1;j<=newtractcoordmat.Nrows();j++){
	  for(int i=1;i<=y1.Nrows();i++){
	    sumOverSeeds(y1(i),j) += newOMmat(r1(i),j);
	  }
	  float maxval,minval;
	  int maxind;
	  minval = sumOverSeeds.Minimum();
	  maxval = sumOverSeeds.Column(j).Maximum1(maxind);
	  if(minval != maxval)
	    opaths(newtractcoordmat(j,1),
		   newtractcoordmat(j,2),
		   newtractcoordmat(j,3)) = maxind;
	}

	
	opaths.setDisplayMaximumMinimum(opaths.max(),0);
	save_volume(opaths,opts.directory.value()+"/tract_space_propagated_"+base);
      }
    }

    // save clustering if kmeans used
    if(opts.scheme.value() == "kmeans" || opts.scheme.value()=="fuzzy"){
      volume<int> mask;
      if(opts.mask.value() == "")
	read_volume(mask,opts.directory.value()+"/fdt_paths");
      else
	read_volume(mask,opts.mask.value());
      mask = 0;
      for(int i=0;i<outcoords.xsize();i++){
	mask(outcoords(i,0,0),
	     outcoords(i,1,0),
	     outcoords(i,2,0)) = (int)y1(i+1);
      }
      mask.setDisplayMaximumMinimum(y1.Maximum(),0);
      save_volume(mask,opts.directory.value()+"/reord_mask_"+base);
      
      // save memberships if fuzzy clustering used
      if(opts.scheme.value() == "fuzzy"){
	volume<float> umask;
	umask.reinitialize(mask.xsize(),mask.ysize(),mask.zsize());
	//OUT(U);
	for(int cl=1;cl<=opts.nclusters.value();cl++){
	  umask=0;
	  for(int i=0;i<outcoords.xsize();i++){
	    //if((int)y1(i+1) == cl){
	      umask(outcoords(i,0,0),
		    outcoords(i,1,0),
		    outcoords(i,2,0)) = U(r1(i+1),cl);
	      //}
	  }
	  umask.setDisplayMaximumMinimum(1,0);
	  save_volume(umask,opts.directory.value()+"/reord_membership_class"+num2str(cl)+"_"+base);
	}
	
      }
	
    }
    
  }

  if(opts.reord2.value()){
    cout<<"Starting Second Reordering"<<endl;
    SymmetricMatrix CC;
    CC << corrcoef(newOMmat);
    CC<<CC+1;

    if(opts.scheme.value()=="spectral")
      spect_reord(CC,r2,y2);
    else if(opts.scheme.value()=="kmeans")
      kmeans_reord(CC,r2,y2,opts.nclusters.value());
    else{
      cerr << "unkown reordering scheme" << endl;
      return(-1);
    }
 
    write_ascii_matrix(r2,opts.directory.value()+"/"+base+"r2");
    write_ascii_matrix(y2,opts.directory.value()+"/"+base+"y2");

    volume<int> outvol(newOMmat.Nrows(),newOMmat.Ncols(),1);
    volume<int> outtractcoords(newtractcoordmat.Nrows(),3,1);


    cout<<"Permuting Matrix"<<endl;
    for(int j=0;j<outvol.ysize();j++){
      for(int i=0;i<outvol.xsize();i++){
	outvol(i,j,0)=(int)newOMmat((int)r1(i+1),(int)r2(j+1));
      }
    }

    if(tractcoordbool){
      cout<<"Permuting Tract Coordinates"<<endl;
      for(int i=0;i<outtractcoords.xsize();i++){
	outtractcoords(i,0,0)=(int)newtractcoordmat(int(r2(i+1)),1);
	outtractcoords(i,1,0)=(int)newtractcoordmat(int(r2(i+1)),2);
	outtractcoords(i,2,0)=(int)newtractcoordmat(int(r2(i+1)),3);
      } 
    }
    save_volume(outvol,opts.directory.value()+"/reord_"+base);
    save_volume(outtractcoords,opts.directory.value()+"/tract_space_coords_for_reord_"+base);
  }

  cout << "Done." << endl;
  return 0;
}