Skip to content
Snippets Groups Projects
meldata.cc 29.6 KiB
Newer Older
Mark Jenkinson's avatar
Mark Jenkinson committed
/*  MELODIC - Multivariate exploratory linear optimized decomposition into 
              independent components
    
    meldata.cc - data handler / container class

    Christian F. Beckmann, FMRIB Image Analysis Group
    
    Copyright (C) 1999-2008 University of Oxford */
Mark Jenkinson's avatar
Mark Jenkinson committed

/*  CCOPYRIGHT  */
Mark Jenkinson's avatar
Mark Jenkinson committed

#include "newimage/newimageall.h"
Mark Jenkinson's avatar
Mark Jenkinson committed
#include "meloptions.h"
#include "meldata.h"
#include "melodic.h" 
#include "utils/log.h"
#include <time.h>
#include "miscmaths/miscprob.h"
#include "melhlprfns.h" 
Mark Jenkinson's avatar
Mark Jenkinson committed

using namespace Utilities;
using namespace NEWIMAGE;
Mark Jenkinson's avatar
Mark Jenkinson committed
namespace Melodic{
// {{{ Setup
Mark Jenkinson's avatar
Mark Jenkinson committed

  Matrix MelodicData::process_file(string fname, int numfiles)
Mark Jenkinson's avatar
Mark Jenkinson committed
  {
    volume4D<float> RawData;

    //read data
    message("Reading data file " << fname << "  ... ");
    read_volume4D(RawData,fname);
    message(" done" << endl);
    del_vols(RawData,opts.dummy.value());
Mark Jenkinson's avatar
Mark Jenkinson committed
    
    Mean += meanvol(RawData)/numfiles;
Mark Jenkinson's avatar
Mark Jenkinson committed
	
    //reshape
    Matrix tmpData;
    tmpData = RawData.matrix(Mask);
    
    //estimate smoothness
Christian Beckmann's avatar
Christian Beckmann committed
    if((Resels == 0)&&(!opts.filtermode))
      Resels = est_resels(RawData,Mask);
        
    //convert to percent BOLD signal change
    if(opts.pbsc.value()){
      message("  Converting data to percent BOLD signal change ...");
      Matrix meanimg = convert_to_pbsc(tmpData);
      meanR = meanimg.Row(1);
      message(" done" << endl);
		if(opts.remove_meanvol.value())
		{	      
			message(string("  Removing mean image ..."));
      		meanR = mean(tmpData);
      		tmpData = remmean(tmpData);
      		message(" done" << endl);
		}
		else meanR=ones(1,tmpData.Ncols());
	if(opts.remove_meantc.value()){
    	meanC = mean(tmpData,2);
		tmpData = remmean(tmpData,2);
	}
	
    //convert to power spectra
    if(opts.pspec.value()){
      message("  Converting data to powerspectra ...");
      tmpData = calc_FFT(tmpData);
      message(" done" << endl);
    }
	
    //switch dimension in case temporal ICA is required
    if(opts.temporal.value()){
      message(string("  Switching dimensions for temporal ICA") << endl);
      tmpData = tmpData.t();
      Matrix tmp;
      tmp = meanC;
      meanC = meanR.t();
      meanR = tmp.t();
      message("  Data size : " << Data.Nrows() << " x " << Data.Ncols() <<endl);
    }
Mark Jenkinson's avatar
Mark Jenkinson committed
      
    //variance - normalisation
    if(opts.varnorm.value()){
      message("  Normalising by voxel-wise variance ..."); 
Christian Beckmann's avatar
Christian Beckmann committed
			if(stdDev.Storage()==0)
      	stdDev = varnorm(tmpData,std::min(30,tmpData.Nrows()-1),
					opts.vn_level.value())/numfiles;
Christian Beckmann's avatar
Christian Beckmann committed
			else 	
				stdDev += varnorm(tmpData,std::min(30,tmpData.Nrows()-1),
					opts.vn_level.value())/numfiles;
      stdDevi = pow(stdDev,-1); 
      message(" done" << endl);
Mark Jenkinson's avatar
Mark Jenkinson committed
    }

    return tmpData;
  }

  Matrix MelodicData::expand_mix()
  {
    Matrix out;
    out = expand_dimred(mixMatrix);
    return out;
  }

  Matrix MelodicData::expand_dimred(const Matrix& Mat)
  {
    int first, last;
    first = 1;
    last = DWM.at(0).Ncols();
    Matrix tmp = DWM.at(0) * Mat.Rows(first,last);
    for(unsigned int ctr = 1; ctr < DWM.size(); ctr++){
      first = last + 1;
      last += DWM.at(ctr).Ncols();
      tmp &= DWM.at(ctr) * Mat.Rows(first, last);
Mark Jenkinson's avatar
Mark Jenkinson committed
    }
    return tmp;
  }

  Matrix MelodicData::reduce_dimred(const Matrix& Mat)
  {
    int first, last;
    first = 1;
    last = WM.at(0).Ncols();
    Matrix tmp = WM.at(0) * Mat.Rows(first,last);
    for(unsigned int ctr = 1; ctr < WM.size(); ctr++){
      first = last + 1;
      last += WM.at(ctr).Ncols();
      tmp &= WM.at(ctr) * Mat.Rows(first, last);
Mark Jenkinson's avatar
Mark Jenkinson committed
    }
  void MelodicData::set_TSmode()
  {
Christian Beckmann's avatar
Christian Beckmann committed
   	dbgmsg(string("START: set_TSmode"));	
	
	Matrix tmp, tmpT, tmpS, tmpT2, tmpS2, tmpT3;
    tmp = expand_dimred(mixMatrix);
    tmpT = zeros(tmp.Nrows()/numfiles, tmp.Ncols());
    tmpS = ones(numfiles, tmp.Ncols());
Christian Beckmann's avatar
Christian Beckmann committed
	outMsize("tmp",tmp);
	outMsize("tmpT",tmpT);
	outMsize("tmpS",tmpS);

  	dbgmsg(string("   approach ") << opts.approach.value() << endl);	

Christian Beckmann's avatar
Christian Beckmann committed
	if(opts.approach.value()!=string("concat")){
      message("Calculating T- and S-modes " << endl);
      explained_var = krfact(tmp,tmpT,tmpS);
      Tmodes.clear(); Smodes.clear();
      for(int ctr = 1; ctr <= tmp.Ncols(); ctr++){
	    tmpT3 << reshape(tmp.Column(ctr),tmpT.Nrows(),numfiles);
		outMsize("tmpT3", tmpT3);
      	tmpT2 << tmpT.Column(ctr);
      	tmpS2 << tmpS.Column(ctr);
		tmpT3 << SP(tmpT3,pow(ones(tmpT3.Nrows(),1)*tmpS2.t(),-1));
		if(numfiles>1)
	      tmpT2 |= tmpT3;
		if(mean(tmpS2,1).AsScalar()<0){
		  tmpT2*=-1.0;
		  tmpS2*=-1.0;
      	add_Tmodes(tmpT2);
      	add_Smodes(tmpS2);
	  }
Christian Beckmann's avatar
Christian Beckmann committed
	}
Christian Beckmann's avatar
Christian Beckmann committed
	else{
		Tmodes.clear();
		Smodes.clear();
		for(int ctr = 1; ctr <= tmp.Ncols(); ctr++){
			tmpT3 << tmp.Column(ctr);
			add_Tmodes(tmpT3);
		}
Christian Beckmann's avatar
Christian Beckmann committed
	}
    if(opts.approach.value()!=string("concat")){
	  //add GLM OLS fit
	  dbgmsg(string(" GLM fitting ") << endl);

	  if(Tdes.Storage()){
	    Matrix alltcs = Tmodes.at(0).Column(1);
	    for(int ctr=1; ctr < (int)Tmodes.size();ctr++)
		  alltcs|=Tmodes.at(ctr).Column(1);
	    if((alltcs.Nrows()==Tdes.Nrows())&&(Tdes.Nrows()>Tdes.Ncols()))
		  glmT.olsfit(alltcs,Tdes,Tcon);
	  }
	  if(Sdes.Storage()){
	    Matrix alltcs = Smodes.at(0);
	    for(int ctr=1; ctr < (int)Smodes.size();ctr++)
	  	  alltcs|=Smodes.at(ctr);
	    if((alltcs.Nrows()==Sdes.Nrows())&&(Sdes.Nrows()>Sdes.Ncols()&&alltcs.Nrows()>2))
		  glmS.olsfit(alltcs,Sdes,Scon);
	  }
		
    }
  //    else{
//		dbgmsg(string(" Bypassing krfac ") << endl);
//        add_Tmodes(tmp);
//		add_Smodes(tmpS);
//      }
	dbgmsg(string("END: set_TSmode"));	
  }

  void MelodicData::setup()
  { 
    numfiles = (int)opts.inputfname.value().size();
Christian Beckmann's avatar
Christian Beckmann committed
    setup_misc();

	if(opts.filtermode){ // basic setup for filtering only
		Data = process_file(opts.inputfname.value().at(0));
	}
	else{
Christian Beckmann's avatar
Christian Beckmann committed

      	if((numfiles > 1) && (opts.approach.value()==string("defl") || opts.approach.value()==string("symm")))
        	opts.approach.set_T("tica");
    	Matrix alldat, tmpData;
Christian Beckmann's avatar
Christian Beckmann committed
		bool tmpvarnorm = opts.varnorm.value();

		if(numfiles > 1 && opts.joined_vn.value()){
Christian Beckmann's avatar
Christian Beckmann committed
			opts.varnorm.set_T(false);
		}
    	alldat = process_file(opts.inputfname.value().at(0), numfiles) / numfiles;
Christian Beckmann's avatar
Christian Beckmann committed

		if(opts.pca_dim.value() > alldat.Nrows()-2){
			cerr << "ERROR:: too many components selected \n\n";
			exit(2);
		}
		
Christian Beckmann's avatar
Christian Beckmann committed
 		if(opts.debug.value())
			save4D(alldat,string("preproc_dat") + num2str(1));   
		for(int ctr = 1; ctr < numfiles; ctr++){
    		tmpData = process_file(opts.inputfname.value().at(ctr), numfiles) / numfiles;
Christian Beckmann's avatar
Christian Beckmann committed
			if(opts.debug.value())
				save4D(tmpData,string("preproc_dat") + num2str(ctr+1));
			if(tmpData.Ncols() == alldat.Ncols() && tmpData.Nrows() == alldat.Nrows())
				    if(opts.approach.value()==string("tica")){
						cerr << "ERROR:: data dimensions do not match, TICA not possible \n\n";
						exit(2); 
					}
					
					if(tmpData.Ncols() == alldat.Ncols()){
						int mindim = min(alldat.Nrows(),tmpData.Nrows());
						alldat = alldat.Rows(1,mindim);
						tmpData = tmpData.Rows(1,mindim);
						alldat += tmpData;	
					}				
					else 	
					message("Data dimensions do not match - ignoring "+opts.inputfname.value().at(ctr) << endl);
				}
   		}	
    	//update mask
    	if(opts.update_mask.value()){
      		message("Excluding voxels with constant value ...");
      		update_mask(Mask, alldat); 
      		message(" done" << endl);
    	}
Christian Beckmann's avatar
Christian Beckmann committed

		if((numfiles > 1 ) && opts.joined_vn.value() && tmpvarnorm){	
Christian Beckmann's avatar
Christian Beckmann committed
			//variance - normalisation
	    	message(endl<<"Normalising jointly by voxel-wise variance ..."); 
	    	stdDev = varnorm(alldat,alldat.Nrows(),3.1);
	    	stdDevi = pow(stdDev,-1); 
	    	message(" done" << endl);
		if(numfiles>1)
    		message(endl << "Initial data size : "<<alldat.Nrows()<<" x "<<alldat.Ncols()<<endl<<endl);
Christian Beckmann's avatar
Christian Beckmann committed
		if(opts.debug.value())
			save4D(alldat,"alldat");
    	//estimate model order
    	Matrix tmpPPCA;
    	RowVector AdjEV, PercEV;
    	Matrix Corr, tmpE;
    	int order;
Christian Beckmann's avatar
Christian Beckmann committed
		//	cerr << "here1" << endl;
    	order = ppca_dim(remmean(alldat,2), RXweight, tmpPPCA, AdjEV, PercEV, Corr, pcaE, pcaD, Resels, opts.pca_est.value());	  
		if (opts.paradigmfname.value().length()>0)
			order += param.Ncols();
Christian Beckmann's avatar
Christian Beckmann committed
		// cerr << "here2" << endl;		
	  	if(opts.pca_dim.value() == 0){
      		opts.pca_dim.set_T(order);
			PPCA=tmpPPCA;
Christian Beckmann's avatar
Christian Beckmann committed
	  	if(opts.pca_dim.value() < 0){
      		opts.pca_dim.set_T(min(order,-1*opts.pca_dim.value()));
			PPCA=tmpPPCA;
  		}
    	order = opts.pca_dim.value();
		if(opts.debug.value())
			message(endl << "Model order : "<<order<<endl<<endl);
Christian Beckmann's avatar
Christian Beckmann committed

Christian Beckmann's avatar
Christian Beckmann committed
		if (opts.paradigmfname.value().length()>0){
			Matrix tmpPscales;
			tmpPscales = param.t() * alldat;
			paramS = stdev(tmpPscales.t());
		    
    		calc_white(pcaE, pcaD, order, param, paramS, whiteMatrix, dewhiteMatrix);
		}else
    		calc_white(pcaE, pcaD, order, whiteMatrix, dewhiteMatrix);

		if(opts.debug.value()){
Christian Beckmann's avatar
Christian Beckmann committed
			outMsize("pcaE",pcaE); saveascii(pcaE,"pcaE");
			outMsize("pcaD",pcaD); saveascii(pcaD,"pcaD");
			outMsize("AdjEV",AdjEV); saveascii(AdjEV,"AdjEV");
			outMsize("PercEV",PercEV); saveascii(PercEV,"PercEV");
			outMsize("tmpPPCA",tmpPPCA); saveascii(tmpPPCA,"tmpPPCA");
			outMsize("whiteMatrix",whiteMatrix); saveascii(whiteMatrix,"whiteMatrix");
			outMsize("dewhiteMatrix",dewhiteMatrix); saveascii(dewhiteMatrix,"dewhiteMatrix");
			cerr << "Order: " << order << endl;
		}

		EV = AdjEV;
		EVP = PercEV;

    	if(numfiles == 1){
      		Data = alldat;
      		Matrix tmp = IdentityMatrix(Data.Nrows());
      		DWM.push_back(tmp);
      		WM.push_back(tmp);
    	} 
Christian Beckmann's avatar
Christian Beckmann committed
		else {
Christian Beckmann's avatar
Christian Beckmann committed
			//cerr << "here" << endl;
      		for(int ctr = 0; ctr < numfiles; ctr++){
Christian Beckmann's avatar
Christian Beckmann committed
				tmpData = process_file(opts.inputfname.value().at(ctr), numfiles);
				if(opts.joined_vn.value() && tmpvarnorm){
Christian Beckmann's avatar
Christian Beckmann committed
					tmpData=SP(tmpData,pow(ones(tmpData.Nrows(),1)*stdDev,-1));
				}
Christian Beckmann's avatar
Christian Beckmann committed
				//  whiten (separate / joint)
				Matrix newWM,newDWM; 
Christian Beckmann's avatar
Christian Beckmann committed
				if(!opts.joined_whiten.value()){	  
					message("  Individual whitening in a " << order << " dimensional subspace " << endl);
      	  			std_pca(tmpData, RXweight, Corr, pcaE, pcaD);
	  				calc_white(pcaE, pcaD, order, newWM, newDWM);
Christian Beckmann's avatar
Christian Beckmann committed
				}else{
					if(!opts.dr_pca.value()){
						std_pca(whiteMatrix*tmpData, RXweight, Corr, pcaE, pcaD);
						calc_white(pcaE, pcaD, order, newWM, newDWM);		
						newDWM=(dewhiteMatrix*newDWM);
						newWM=(newWM*whiteMatrix);
					}
					else{
					  if(opts.debug.value())
					    message(" --mod_pca ");
						tmp1 = whiteMatrix * alldat;
						tmp1 = remmean(tmp1,2) * tmpData.t();
						tmp2 = pinv(tmp1.t()).t();  
						std_pca(tmp1 * tmpData, RXweight, Corr, pcaE, pcaD);
						calc_white(pcaE, pcaD, order, newWM, newDWM);		
						newDWM=(tmp2*newDWM);
						newWM=(newWM * tmp1);
					}
Christian Beckmann's avatar
Christian Beckmann committed
				DWM.push_back(newDWM);
				WM.push_back(newWM);
				tmpData = newWM * tmpData;
Christian Beckmann's avatar
Christian Beckmann committed
				//concatenate Data
				if(Data.Storage() == 0)
	  			Data = tmpData;
				else
	  			Data &= tmpData;
Christian Beckmann's avatar
Christian Beckmann committed
		opts.varnorm.set_T(tmpvarnorm);
    	message(endl << "  Data size : "<<Data.Nrows()<<" x "<<Data.Ncols()<<endl<<endl);
Christian Beckmann's avatar
Christian Beckmann committed
 		outMsize("stdDev",stdDev);
   
    	//meanC=mean(Data,2);
Christian Beckmann's avatar
Christian Beckmann committed
		if(opts.debug.value())
			save4D(Data,"concat_data");    
    	//save the mean & mask
    	save_volume(Mask,logger.appendDir("mask"));
    	save_volume(Mean,logger.appendDir("mean"));
Christian Beckmann's avatar
Christian Beckmann committed
    }
  } // void setup()
Christian Beckmann's avatar
Christian Beckmann committed
	
  void MelodicData::setup_misc()
  {

    //initialize Mean
Matthew Webster's avatar
Matthew Webster committed
    read_volume(Mean,opts.inputfname.value().at(0));

    //create mask
    create_mask(Mask);

Christian Beckmann's avatar
Christian Beckmann committed
	//setup background image
	if(opts.bgimage.value()>""){
		read_volume(background,opts.bgimage.value());
    	if(!samesize(Mean,background)){
        	cerr << "ERROR:: background image and data have different dimensions  \n\n";
        	exit(2);
    	}
	}else{
		background = Mean;
	}
    if(!samesize(Mean,Mask)){
      cerr << "ERROR:: mask and data have different dimensions  \n\n";
      exit(2);
    }

    //reset mean
    Mean *= 0;
     
    //set up weighting
Mark Jenkinson's avatar
Mark Jenkinson committed
    if(opts.segment.value().length()>0){
      create_RXweight();
Mark Jenkinson's avatar
Mark Jenkinson committed
    }

    //seed the random number generator
    double tmptime = time(NULL);
    if ( opts.seed.value() != -1 ) {
      tmptime = opts.seed.value(); 
    }
    srand((unsigned int) tmptime);
Christian Beckmann's avatar
Christian Beckmann committed
	if(opts.paradigmfname.value().length()>0){
		message("  Use columns in " << opts.paradigmfname.value() 
	      << " for PCA initialisation" <<endl);
		param = read_ascii_matrix(opts.paradigmfname.value());
Christian Beckmann's avatar
Christian Beckmann committed
	    Matrix tmpPU, tmpPV;
		DiagonalMatrix tmpPD;
		SVD(param, tmpPD, tmpPU, tmpPV);
		param = tmpPU;
		
		opts.pca_dim.set_T(std::max(opts.pca_dim.value(), param.Ncols()+3));		
		if(opts.debug.value()){
			outMsize("Paradigm",param); saveascii(param,"param");
Christian Beckmann's avatar
Christian Beckmann committed
		}
		//opts.guessfname.set_T(opts.paradigmfname.value());
Christian Beckmann's avatar
Christian Beckmann committed
	}

	//read in post-proc design matrices etc
	if(opts.fn_Tdesign.value().length()>0)
		Tdes = read_ascii_matrix(opts.fn_Tdesign.value());
	if(opts.fn_Sdesign.value().length()>0)
		Sdes = read_ascii_matrix(opts.fn_Sdesign.value());
	if(opts.fn_Tcon.value().length()>0)
		Tcon = read_ascii_matrix(opts.fn_Tcon.value());
	if(opts.fn_Scon.value().length()>0)
		Scon = read_ascii_matrix(opts.fn_Scon.value());
	if(opts.fn_TconF.value().length()>0)
		TconF = read_ascii_matrix(opts.fn_TconF.value());
	if(opts.fn_SconF.value().length()>0)
		SconF = read_ascii_matrix(opts.fn_SconF.value());
		
	if(numfiles>1 && Sdes.Storage() == 0){
 		Sdes = ones(numfiles,1);
		if(Scon.Storage() == 0){
			Scon = ones(1,1);
			Scon &= -1*Scon;
		}
	}
	Tdes = remmean(Tdes,1);
Christian Beckmann's avatar
Christian Beckmann committed
  }
Mark Jenkinson's avatar
Mark Jenkinson committed

  void MelodicData::save()
  {   

    //check for temporal ICA
    if(opts.temporal.value()){
      message(string("temporal ICA: transform back the data ... "));
Mark Jenkinson's avatar
Mark Jenkinson committed
      Matrix tmpIC = mixMatrix.t();
      mixMatrix=IC.t();
      IC=tmpIC;

      unmixMatrix=pinv(mixMatrix);
      Data = Data.t();
      tmpIC = meanC;
      meanC = meanR.t();
      meanR = tmpIC.t();
      //  whiteMatrix = whiteMatrix.t;
      //  dewhiteMatrix = dewhiteMatrix.t();
      message(string("done") << endl);
      opts.temporal.set_T(false); // Do not switch again!
    message(endl << "Writing results to : " << endl);
Mark Jenkinson's avatar
Mark Jenkinson committed

    //Output IC	
    if((IC.Storage()>0)&&(opts.output_origIC.value())&&(after_mm==false))
      save4D(IC,opts.outputfname.value() + "_oIC");
      
Mark Jenkinson's avatar
Mark Jenkinson committed
    //Output IC -- adjusted for noise	
      if(IC.Storage()>0){
Christian Beckmann's avatar
Christian Beckmann committed
				volume4D<float> tempVol;	
Christian Beckmann's avatar
Christian Beckmann committed
				//Matrix ICadjust;
				if(after_mm){
	  			save4D(IC,opts.outputfname.value() + "_IC");
	  			// ICadjust = IC;
				}	
				else{
Christian Beckmann's avatar
Christian Beckmann committed
					Matrix resids = stdev(Data - mixMatrix * IC);
					for(int ctr=1;ctr<=resids.Ncols();ctr++)
						if(resids(1,ctr) < 0.05)
							resids(1,ctr)=1;
	  //			stdNoisei = pow(stdev(Data - mixMatrix * IC)*
		//				std::sqrt((float)(Data.Nrows()-1))/
		//				std::sqrt((float)(Data.Nrows()-IC.Nrows())),-1);
	  			stdNoisei = pow(resids*
Christian Beckmann's avatar
Christian Beckmann committed
						std::sqrt((float)(Data.Nrows()-1))/
						std::sqrt((float)(Data.Nrows()-IC.Nrows())),-1);
Christian Beckmann's avatar
Christian Beckmann committed
	  			ColumnVector diagvals;
	  			diagvals=pow(diag(unmixMatrix*unmixMatrix.t()),-0.5);
Christian Beckmann's avatar
Christian Beckmann committed
	  			save4D(SP(IC,diagvals*stdNoisei),opts.outputfname.value() + "_IC");
				}
Christian Beckmann's avatar
Christian Beckmann committed
				if(opts.output_origIC.value())
	  			save4D(stdNoisei,string("Noise_stddev_inv"));
      }
Christian Beckmann's avatar
Christian Beckmann committed
     
    //Output T- & S-modes
Christian Beckmann's avatar
Christian Beckmann committed
    save_Tmodes();
    save_Smodes();
Christian Beckmann's avatar
Christian Beckmann committed

Mark Jenkinson's avatar
Mark Jenkinson committed
    //Output mixMatrix
    if(mixMatrix.Storage()>0){
Christian Beckmann's avatar
Christian Beckmann committed
      saveascii(expand_mix(), opts.outputfname.value() + "_mix");
Christian Beckmann's avatar
Christian Beckmann committed
      mixFFT=calc_FFT(expand_mix(), opts.logPower.value());
      saveascii(mixFFT,opts.outputfname.value() + "_FTmix");      
Christian Beckmann's avatar
Christian Beckmann committed
    //Output PPCA
    if(PPCA.Storage()>0)
      saveascii(PPCA, opts.outputfname.value() + "_PPCA");
  
    if(ICstats.Storage()>0)
      saveascii(ICstats,opts.outputfname.value() + "_ICstats"); 
      
Mark Jenkinson's avatar
Mark Jenkinson committed
    //Output unmixMatrix
    if(opts.output_unmix.value() && unmixMatrix.Storage()>0)
      saveascii(unmixMatrix,opts.outputfname.value() + "_unmix");
Mark Jenkinson's avatar
Mark Jenkinson committed

    //Output Mask
    message("  "<< logger.appendDir("mask") <<endl);

    //Output mean
    if(opts.output_mean.value() && meanC.Storage()>0 && meanR.Storage()>0){
      saveascii(meanR,opts.outputfname.value() + "_meanR");
      saveascii(meanC,opts.outputfname.value() + "_meanC");
Mark Jenkinson's avatar
Mark Jenkinson committed
    }

    //Output white
    if(opts.output_white.value() && whiteMatrix.Storage()>0&&
Christian Beckmann's avatar
Christian Beckmann committed
      dewhiteMatrix.Storage()>0){
      	saveascii(whiteMatrix,opts.outputfname.value() + "_white");
      	saveascii(dewhiteMatrix,opts.outputfname.value() + "_dewhite");
      	Matrix tmp;
      	tmp=calc_FFT(dewhiteMatrix, opts.logPower.value());
      	saveascii(tmp,opts.outputfname.value() + "_FTdewhite");
Mark Jenkinson's avatar
Mark Jenkinson committed

    //Output PCA
    if(opts.output_pca.value() && pcaD.Storage()>0&&pcaE.Storage()>0){
      saveascii(pcaE,opts.outputfname.value() + "_pcaE");
      saveascii((Matrix) diag(pcaD),opts.outputfname.value() + "_pcaD");
Mark Jenkinson's avatar
Mark Jenkinson committed
      Matrix PCAmaps;
Christian Beckmann's avatar
Christian Beckmann committed
      if(whiteMatrix.Ncols()==Data.Ncols())
				PCAmaps = dewhiteMatrix.t();
      else
				PCAmaps = whiteMatrix * Data;
Mark Jenkinson's avatar
Mark Jenkinson committed

      save4D(PCAmaps,opts.outputfname.value() + "_pca");
    }
 
		message("...done" << endl);
  } //void save()
Mark Jenkinson's avatar
Mark Jenkinson committed
  int MelodicData::remove_components()
  {  
    message("Reading " << opts.filtermix.value() << endl) 
    mixMatrix = read_ascii_matrix(opts.filtermix.value());
    if (mixMatrix.Storage()<=0) {
      cerr <<" Please specify the mixing matrix correctly" << endl;
      exit(2);
    }
    
    unmixMatrix = pinv(mixMatrix);
    IC = unmixMatrix * Data;

    string tmpstr;
    tmpstr = opts.filter.value();

    Matrix noiseMix;
    Matrix noiseIC;

    int ctr=0;    
    char *p;
    char t[1024];
    const char *discard = ", [];{(})abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*_-=+|\':><./?";
    
    message("Filtering the data...");
    strcpy(t, tmpstr.c_str());
    p=strtok(t,discard);
    ctr = atoi(p);
    if(ctr>0 && ctr<=mixMatrix.Ncols()){
      message(" "<< ctr );
      noiseMix = mixMatrix.Column(ctr);
      noiseIC  = IC.Row(ctr).t();    
Christian Beckmann's avatar
Christian Beckmann committed
    }
    else{
Mark Jenkinson's avatar
Mark Jenkinson committed
      cerr << endl<< "component number "<<ctr<<" does not exist" << endl;
    }
    
    do{
      p=strtok(NULL,discard);
      if(p){
Christian Beckmann's avatar
Christian Beckmann committed
				ctr = atoi(p);
Mark Jenkinson's avatar
Mark Jenkinson committed
	
        if(ctr>0 && ctr<=mixMatrix.Ncols()){
Christian Beckmann's avatar
Christian Beckmann committed
	  			message(" "<<ctr);
	  			noiseMix |= mixMatrix.Column(ctr);
	  			noiseIC  |= IC.Row(ctr).t();
				}
				else{
	  			cerr << endl<< "component number "<<ctr<<" does not exist" << endl;
				}
Mark Jenkinson's avatar
Mark Jenkinson committed
      }
    }while(p);
    message(endl);
    Matrix newData;

		outMsize("DATA",Data);
		outMsize("IC",IC);
		outMsize("noiseIC",noiseIC);
		outMsize("noiseMix",noiseMix);
		outMsize("meanR",meanR);
		outMsize("meanC",meanC);

Mark Jenkinson's avatar
Mark Jenkinson committed
    newData = Data - noiseMix * noiseIC.t();
Christian Beckmann's avatar
Christian Beckmann committed

		if(meanR.Storage()>0)
    	newData = newData + ones(newData.Nrows(),1)*meanR;
Mark Jenkinson's avatar
Mark Jenkinson committed
    
    volume4D<float> tmp;
    read_volume4D(tmp,opts.inputfname.value().at(0)); 
Mark Jenkinson's avatar
Mark Jenkinson committed
    tmp.setmatrix(newData,Mask);
    save_volume4D(tmp,logger.appendDir(opts.outputfname.value() + "_ICAfiltered")); 
   
    return 0;
  } // int remove_components()
Mark Jenkinson's avatar
Mark Jenkinson committed
  void MelodicData::create_RXweight()
  {
    message("Reading the weights for the covariance R_X from file "<< opts.segment.value() << endl);
  
    volume4D<float> tmpRX;
    read_volume4D(tmpRX,opts.segment.value());
    RXweight = tmpRX.matrix(Mask);
  } 
Christian Beckmann's avatar
Christian Beckmann committed
  void MelodicData::est_smoothness()
  {
    if(Resels == 0){
      string SM_path = opts.binpath + "smoothest";
      string Mask_fname = logger.appendDir("mask");

      if(opts.segment.value().length()>0){
Christian Beckmann's avatar
Christian Beckmann committed
				Mask_fname =  opts.segment.value();
      } 

      // Setup external call to smoothest:
      char callSMOOTHESTstr[1000];
      ostrstream osc(callSMOOTHESTstr,1000);
      osc  << SM_path << " -d " << data_dim()
Christian Beckmann's avatar
Christian Beckmann committed
	   		<< " -r " << opts.inputfname.value().at(0) << " -m " 
	   		<< Mask_fname << " > " << logger.appendDir("smoothest") << '\0';
      
      message("  Calling Smoothest: " << callSMOOTHESTstr << endl);
      system(callSMOOTHESTstr);

      //read back the results
      ifstream in;
      string str;
      Resels = 1.0;
      
      in.open(logger.appendDir("smoothest").c_str(), ios::in);
      if(in>0){
Christian Beckmann's avatar
Christian Beckmann committed
				for(int ctr=1; ctr<7; ctr++)
					in >> str;
				in.close();
				if(str!="nan")
	  			Resels = atof(str.c_str());
Christian Beckmann's avatar
Christian Beckmann committed
  unsigned long MelodicData::standardise(volume<float>& mask, volume4D<float>& R)
  {
Christian Beckmann's avatar
Christian Beckmann committed
    	unsigned long count = 0;
    	int M=R.tsize();
Christian Beckmann's avatar
Christian Beckmann committed
    	for (int z=mask.minz(); z<=mask.maxz(); z++) {
      	for (int y=mask.miny(); y<=mask.maxy(); y++) {
					for (int x=mask.minx(); x<=mask.maxx(); x++) {
	  				if( mask(x,y,z) > 0.5) {
	    				count ++;
	    				if( M > 2 ) {
	      				// For each voxel 
	      				//    calculate mean and standard deviation...
	      				double Sx = 0.0, SSx = 0.0;	      
	      				for ( int t = 0; t < M; t++ ) {
									float R_it = R(x,y,z,t);
									Sx += R_it;
									SSx += (R_it)*(R_it);
	      				}
Christian Beckmann's avatar
Christian Beckmann committed
	      				float mean = Sx / M;
	      				float sdsq = (SSx - ((Sx)*(Sx) / M)) / (M - 1) ;
Christian Beckmann's avatar
Christian Beckmann committed
	      				if (sdsq<=0) {
									// trap for differences between mask and invalid data
									mask(x,y,z)=0;
									count--;
	      				} else {
	      					//    ... and use them to standardise to N(0, 1).
									for ( unsigned short t = 0; t < M; t++ ) {
		  							R(x,y,z,t) = (R(x,y,z,t) - mean) / sqrt(sdsq);
									}
	      				} 
	    				}
	  				}
					}
      	}
    	}  
    	return count;

  float MelodicData::est_resels(volume4D<float> R, volume<float> mask)
  {
    message("  Estimating data smoothness ... ");
    unsigned long mask_volume = standardise(mask, R);

    int dof = R.tsize();
    unsigned long N = mask_volume;

    // MJ additions to make it cope with 2D images
    bool usez = true;
    if (R.zsize() <= 1) { usez = false; }

    enum {X = 0, Y, Z};
    float SSminus[3] = {0, 0, 0}, S2[3] = {0, 0, 0};

    int zstart=1;
    if (!usez) zstart=0;
    for ( unsigned short z = zstart; z < R.zsize() ; z++ )
      for ( unsigned short y = 1; y < R.ysize() ; y++ )
Christian Beckmann's avatar
Christian Beckmann committed
				for ( unsigned short x = 1; x < R.xsize() ; x++ )
	  			// Sum over N
	  			if( (mask(x, y, z)>0.5) &&
	      		(mask(x-1, y, z)>0.5) && 
	      		(mask(x, y-1, z)>0.5) && 
	      		( (!usez) || (mask(x, y, z-1)>0.5) ) ) {
Christian Beckmann's avatar
Christian Beckmann committed
	    				N++;
Christian Beckmann's avatar
Christian Beckmann committed
	    				for ( unsigned short t = 0; t < R.tsize(); t++ ) {
	      				// Sum over M
	      				SSminus[X] += R(x, y, z, t) * R(x-1, y, z, t);
	      				SSminus[Y] += R(x, y, z, t) * R(x, y-1, z, t);
	      				if (usez) SSminus[Z] += R(x, y, z, t) * R(x, y, z-1, t);

	      				S2[X] += 0.5 * (R(x, y, z, t)*R(x, y, z, t) + 
									R(x-1, y, z, t)*R(x-1, y, z, t));
	      				S2[Y] += 0.5 * (R(x, y, z, t)*R(x, y, z, t) + 
									R(x, y-1, z, t)*R(x, y-1, z, t));
	      				if (usez) S2[Z] += 0.5 * (R(x, y, z, t)*R(x, y, z, t) + 
									R(x, y, z-1, t)*R(x, y, z-1, t));
	    				}

    float norm = 1.0/(float) N;
    float v = dof;	// v - degrees of freedom (nu)  
    if(R.tsize() > 1) {
      norm = (v - 2) / ((v - 1) * N * R.tsize());
    }
Mark Jenkinson's avatar
Mark Jenkinson committed
 
    // for extreme smoothness 
    if (SSminus[X]>=0.99999999*S2[X]) 
      SSminus[X]=0.99999*S2[X];  
    if (SSminus[Y]>=0.99999999*S2[Y]) 
      SSminus[Y]=0.99999*S2[Y];
    if (usez) 
      if (SSminus[Z]>=0.99999999*S2[Z]) 
Christian Beckmann's avatar
Christian Beckmann committed
				SSminus[Z]=0.99999*S2[Z];
    // Convert to sigma squared
    float sigmasq[3] = {0,0,0};
    sigmasq[X] = -1.0 / (4 * log(fabs(SSminus[X]/S2[X])));
    sigmasq[Y] = -1.0 / (4 * log(fabs(SSminus[Y]/S2[Y])));
    if (usez) { sigmasq[Z] = -1.0 / (4 * log(fabs(SSminus[Z]/S2[Z]))); }
    
    // Convert to full width half maximum
    float FWHM[3] = {0,0,0};
    FWHM[X] = sqrt(8 * log(2) * sigmasq[X]);
    FWHM[Y] = sqrt(8 * log(2) * sigmasq[Y]);
    if (usez) { FWHM[Z] = sqrt(8 * log(2) * sigmasq[Z]); }
    float resels = FWHM[X] * FWHM[Y];
    if (usez) resels *= FWHM[Z];

    message(" done " <<endl);
    return resels;
  }
  void MelodicData::create_mask(volume<float>& theMask)
Mark Jenkinson's avatar
Mark Jenkinson committed
  {
    if(opts.use_mask.value() && opts.maskfname.value().size()>0){   // mask provided 
      read_volume(theMask,opts.maskfname.value());
Christian Beckmann's avatar
Christian Beckmann committed
      message("Mask provided : " << opts.maskfname.value()<<endl<<endl);
Mark Jenkinson's avatar
Mark Jenkinson committed
    }
    else{
      if(opts.perf_bet.value() && opts.use_mask.value()){ //use BET
Christian Beckmann's avatar
Christian Beckmann committed
				message("Create mask ... ");
    		//save first image
    		tmpnam(Mean_fname); // generate a tmp name
    		save_volume(Mean,Mean_fname);    

Christian Beckmann's avatar
Christian Beckmann committed
				// set up all strings
				string BET_outputfname = string(Mean_fname)+"_brain";
Mark Jenkinson's avatar
Mark Jenkinson committed

Christian Beckmann's avatar
Christian Beckmann committed
				string BET_path = opts.binpath + "bet";
				string BET_optarg = "-m -f 0.4"; // see man bet
				string Mask_fname = BET_outputfname+"_mask";
Mark Jenkinson's avatar
Mark Jenkinson committed

Christian Beckmann's avatar
Christian Beckmann committed
				// Setup external call to BET:
Mark Jenkinson's avatar
Mark Jenkinson committed

		//		char callBETstr[1000];
	//			ostrstream betosc(callBETstr,1000);
//				betosc  << BET_path << " " << Mean_fname << " " 
//	     		<< BET_outputfname << " " << BET_optarg << " > /dev/null " << '\0';
Mark Jenkinson's avatar
Mark Jenkinson committed
	
//        message("  Calling BET: " << callBETstr << endl);
//				system(callBETstr);

				string tmpstr = BET_path + string(" ") + 
				                Mean_fname + string(" ") + BET_outputfname + string(" ") + 
				                BET_optarg + string(" > /dev/null ");
				system(tmpstr.c_str());
								
Christian Beckmann's avatar
Christian Beckmann committed
				// read back the Mask file   
				read_volume(theMask,Mask_fname);
				
				// clean /tmp
		    char callRMstr[1000];
		    ostrstream osc(callRMstr,1000);
		    osc  << "rm " << string(Mean_fname) <<"*  " << '\0';
		    system(callRMstr);
		   
Christian Beckmann's avatar
Christian Beckmann committed
				message("done" << endl);
Mark Jenkinson's avatar
Mark Jenkinson committed
      }  
      else{
Christian Beckmann's avatar
Christian Beckmann committed
				if(opts.use_mask.value()){   //just threshold the Mean
	  			message("Create mask ... ");
	  			float Mmin, Mmax, Mtmp;
	  			Mmin = Mean.min(); Mmax = Mean.max();
	  			theMask = binarise(Mean,Mmin + opts.threshold.value()* 
						(Mmax-Mmin),Mmax);
Mark Jenkinson's avatar
Mark Jenkinson committed
          Mtmp = Mmin + opts.threshold.value()* (Mmax-Mmin);
Christian Beckmann's avatar
Christian Beckmann committed
	  			message("done" << endl);
				}
				else{ //well, don't threshold then
Christian Beckmann's avatar
Christian Beckmann committed
	  		  theMask = Mean;
	  		  theMask = 1.0;
Christian Beckmann's avatar
Christian Beckmann committed
				}
Mark Jenkinson's avatar
Mark Jenkinson committed
      }
    }
    if(opts.remove_endslices.value()){ 
      // just in case mc introduced something nasty
      message("  Deleting end slices" << endl);
      for(int ctr1=theMask.miny(); ctr1<=theMask.maxy(); ctr1++){
Christian Beckmann's avatar
Christian Beckmann committed
				for(int ctr2=theMask.minx(); ctr2<=theMask.maxx(); ctr2++){   
	  			theMask(ctr2,ctr1,Mask.minz()) = 0.0;
	  			theMask(ctr2,ctr1,Mask.maxz()) = 0.0;
				}
Mark Jenkinson's avatar
Mark Jenkinson committed
      }
    }
  } //void create_mask()
Christian Beckmann's avatar
Christian Beckmann committed
  void MelodicData::sort()
  {
Christian Beckmann's avatar
Christian Beckmann committed
    int numComp = mixMatrix.Ncols(), numVox = IC.Ncols(), 
        numTime = mixMatrix.Nrows(), i,j;
	//flip IC maps to be positive (on max)
	//flip Subject/Session modes to be positive (on average)
	//flip time courses accordingly
Christian Beckmann's avatar
Christian Beckmann committed
    for(int ctr_i = 1; ctr_i <= numComp; ctr_i++)
      if(IC.Row(ctr_i).MaximumAbsoluteValue()>IC.Row(ctr_i).Maximum()){
Christian Beckmann's avatar
Christian Beckmann committed
				flipres(ctr_i);	
    message("Sorting IC maps" << endl);  
    Matrix tmpscales, tmpICrow, tmpMIXcol;
	if(numfiles > 1 && opts.approach.value()==string("tica")){
		set_TSmode();
		Matrix allmodes = Smodes.at(0);
		for(int ctr = 1; ctr < (int)Smodes.size();++ctr)
			allmodes |= Smodes.at(ctr);
		tmpscales = median(allmodes).t();	
	} else {
    // re-order wrt standard deviation of IC maps 
Christian Beckmann's avatar
Christian Beckmann committed
		
    ICstats = tmpscales;

    double max_val, min_val = tmpscales.Minimum()-1;

    for(int ctr_i = 1; ctr_i <= numComp; ctr_i++){
      max_val = tmpscales.Maximum2(i,j);
      ICstats(ctr_i,1)=max_val;
  
      tmpICrow = IC.Row(ctr_i);
      tmpMIXcol = mixMatrix.Column(ctr_i);
      
      IC.SubMatrix(ctr_i,ctr_i,1,numVox) = IC.SubMatrix(i,i,1,numVox);
      mixMatrix.SubMatrix(1,numTime,ctr_i,ctr_i) = 
Christian Beckmann's avatar
Christian Beckmann committed
				mixMatrix.SubMatrix(1,numTime,i,i);

      IC.SubMatrix(i,i,1,numVox) = tmpICrow.SubMatrix(1,1,1,numVox);
      mixMatrix.SubMatrix(1,numTime,i,i) = tmpMIXcol.SubMatrix(1,numTime,1,1);
  
      tmpscales(i,1)=tmpscales(ctr_i,1);
      tmpscales(ctr_i,1)=min_val;

    ICstats /= ICstats.Column(1).Sum();
    ICstats *= 100;
    
    if(EVP.Storage()>0){
      tmpscales = ICstats.Column(1).AsMatrix(ICstats.Nrows(),1) * EVP(1,numComp);
      ICstats |= tmpscales;
    }

    if(Data.Storage()>0&&stdDev.Storage()>0){
      Matrix copeP(tmpscales), copeN(tmpscales);
      Matrix max_ICs(tmpscales), min_ICs(tmpscales);
      for(int ctr_i = 1; ctr_i <= numComp; ctr_i++){
Christian Beckmann's avatar
Christian Beckmann committed
				int i,j;
				max_ICs(ctr_i,1) = IC.Row(ctr_i).Maximum2(i,j);
				copeP(ctr_i,1) = std::abs((pinv(mixMatrix)*
					Data.Column(j)).Row(ctr_i).AsScalar()*stdDev(1,j)*100*
					(mixMatrix.Column(ctr_i).Maximum()-
					mixMatrix.Column(ctr_i).Minimum())/meanR(1,j));

				min_ICs(ctr_i,1) = IC.Row(ctr_i).Minimum2(i,j);
				copeN(ctr_i,1) = -1.0*std::abs((pinv(mixMatrix)*
					Data.Column(j)).Row(ctr_i).AsScalar()*stdDev(1,j)*100*
					(mixMatrix.Column(ctr_i).Maximum()-
					mixMatrix.Column(ctr_i).Minimum())/meanR(1,j));
      }
      ICstats |= copeP;
      ICstats |= copeN;
    }
Christian Beckmann's avatar
Christian Beckmann committed
    mixFFT=calc_FFT(expand_mix(), opts.logPower.value());
    unmixMatrix = pinv(mixMatrix);
  }
Mark Jenkinson's avatar
Mark Jenkinson committed

Christian Beckmann's avatar
Christian Beckmann committed
  void MelodicData::reregress()
  {
	if((numfiles > 1)){
	   
	}
  }
Mark Jenkinson's avatar
Mark Jenkinson committed
  void MelodicData::status(const string &txt)
  {
    cout << "MelodicData Object " << txt << endl;
    if(Data.Storage()>0){cout << "Data: " << Data.Nrows() <<"x" << Data.Ncols() << endl;}else{cout << "Data empty " <<endl;}
    if(pcaE.Storage()>0){cout << "pcaE: " << pcaE.Nrows() <<"x" << pcaE.Ncols() << endl;}else{cout << "pcaE empty " <<endl;}
    if(pcaD.Storage()>0){cout << "pcaD: " << pcaD.Nrows() <<"x" << pcaD.Ncols() << endl;}else{cout << "pcaD empty " <<endl;}
    if(whiteMatrix.Storage()>0){cout << "white: " << whiteMatrix.Nrows() <<"x" << whiteMatrix.Ncols() << endl;}else{cout << "white empty " <<endl;}
    if(dewhiteMatrix.Storage()>0){cout << "dewhite: " << dewhiteMatrix.Nrows() <<"x" << dewhiteMatrix.Ncols() << endl;}else{cout << "dewhite empty " <<endl;}
    if(mixMatrix.Storage()>0){cout << "mix: " << mixMatrix.Nrows() <<"x" << mixMatrix.Ncols() << endl;}else{cout << "mix empty " <<endl;}