diff --git a/cPPI/cppi_ConnectomicPPI.m b/cPPI/cppi_ConnectomicPPI.m new file mode 100644 index 00000000..f0dbed98 --- /dev/null +++ b/cPPI/cppi_ConnectomicPPI.m @@ -0,0 +1,235 @@ +function results = cppi_ConnectomicPPI(D0,parameters) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% +%%% +%%% +%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Initial setup and check code borrow from Robert's +% SOM_CalculateCorrelations.m code + +global SOM; +global mcLog + +results = -1; + +parameters.startCPU.cppi = cputime; + +% +% Check the roi parameters +% + +parameters.rois = SOM_CheckROIParameters(parameters); + +if parameters.rois.OK == -1 + SOM_LOG('FATAL ERROR : parameters.rois failed to meet criteria'); + return +end + +% +% Check the output information +% + +parameters.Output = SOM_CheckOutput(parameters); + +if parameters.Output.OK == -1 + SOM_LOG('FATAL ERROR : parameters.Output failed to meet criteria'); + return +end + +% +% Sanity check, if Output.type = 0, which is a correlation map, then you +% need at least 2 ROIs that survived any cleaning up. +% + +if parameters.Output.type == 0 & parameters.rois.nrois < 2 + SOM_LOG('FATAL ERROR : You specified output to be a correlation matrix, but insufficient number of ROIS'); + return +end + +% +% Okay - we can do the work now. +% + +% Take the ROI definitions and turn them into linear indices for +% calculations. + +parameters.rois = SOM_BuildROILinearIDX(parameters); + +% Now do the correlation work, either making maps or images. + +switch parameters.Output.type + + case 0 + % + % Correlation maps. + % + SOM_LOG('STATUS : Calculating Connectomic PPI'); + % Array of ROI time courses. + + roiTC = zeros(size(D0,2),parameters.rois.nroisRequested); + + % Create an array of our data that is Time x ROI# + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %ADJUST DATA FOR CONTRAST + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %need contrast struct from SPM + %need betas from SPM.Vbeta + curwd = pwd; + cd(fileparts(parameters.cppi.SPM)); + load(parameters.cppi.SPM); + + beta = spm_read_vols(SPM.Vbeta); + rbeta = reshape(beta,prod(size(beta(:,:,:,1))),size(beta,4)); + mrbeta = rbeta(parameters.maskInfo.iMask,:); + cd(curwd); + + for iROI = 1 : parameters.rois.nroisRequested + y = D0(parameters.rois.IDX{iROI},:)'; + y = spm_filter(SPM.xX.K,SPM.xX.W*y); + beta = mrbeta(parameters.rois.IDX{iROI},:)'; + + y = y - spm_FcUtil('Y0',SPM.xCon(parameters.cppi.adjust),SPM.xX.xKXs,beta); + + y(isnan(y)) = 0; + %loop over runs + fy = []; + for iRun = 1:size(SPM.Sess,2) + xY.X0 = SPM.xX.xKXs.X(:,[SPM.xX.iB SPM.xX.iG]); + i = SPM.Sess(iRun).row; + ty = y(i,:); + xY.X0 = xY.X0(i,:); + try + xY.X0 = [xY.X0 SPM.xX.K(iRun).X0]; + end + try + xY.X0 = [xY.X0 SPM.xX.K(iRun).KH]; % Compatibility check + end + + %-Remove null space of X0 + %-------------------------------------------------------------------------- + xY.X0 = xY.X0(:,any(xY.X0)); + + %-Compute regional response in terms of first eigenvariate + %-------------------------------------------------------------------------- + [m n] = size(ty); + if m > n + [v s v] = svd(ty'*ty); + s = diag(s); + v = v(:,1); + u = ty*v/sqrt(s(1)); + else + [u s u] = svd(ty*ty'); + s = diag(s); + u = u(:,1); + v = ty'*u/sqrt(s(1)); + end + d = sign(sum(v)); + u = u*d; + v = v*d; + Y = u*sqrt(s(1)/n); + ty = Y; + fy = [fy;ty]; + end + fy(isnan(fy)) = 0; + roiTC(:,iROI) = fy; + %roiTC(:,iROI) = mean(D0(parameters.rois.IDX{iROI},:),1); + end + + % Now loop on the ROIs and calculate PPI models for each one + % separately to build up the cPPI grid. + cppi_grid = []; + load(parameters.cppi.SPM); + for iROI = 1:parameters.rois.nroisRequested + roiTCscaled(:,iROI) = roiTC(:,iROI).*SPM.xGX.gSF; + end + cppi_CreateImages(roiTC,parameters); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %attempt to filter out bad timepoints + pctnonconstant = sum(diff(roiTCscaled)~=0)./(size(roiTCscaled,1)-1); + badmask = pctnonconstant<.75; + roiTCscaled(:,badmask) = 0; + roiTC(:,badmask) = 0; + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + logstring = sprintf('%s: %d ROIs total',datestr(now),parameters.rois.nroisRequested); + mc_Logger('log',logstring,3); + for iROI = 1:parameters.rois.nroisRequested + logstring = sprintf('%s: Working on ROI %d',datestr(now),iROI); + mc_Logger('log',logstring,3); + %[cppiregressors betanames] = + %cppi_CreateRegressors_spm(parameters.rois.mni.coordinates(iROI,:),parameters,roiTC(:,iROI)); + %roiTCtemp = roiTC(:,iROI).*SPM.xGX.gSF; + roiTCscaled(isnan(roiTCscaled)) = 0; + [cppiregressors betanames] = cppi_CreateRegressors(roiTCscaled(:,iROI),parameters); + for iB = 1:size(betanames,2) + cppi_grid{1,iB} = betanames{iB}; + end + + try + model = cppi_CreateModel(cppiregressors,roiTC,parameters); + [cppi_grid result] = cppi_Extract(cppiregressors,model,parameters,cppi_grid,iROI,roiTC); + catch err + model = parameters.cppi.sandbox; + + nummotion = size(parameters.data.run(1).MotionParameters,2); + domotion = parameters.cppi.domotion; + numrun = size(parameters.data.run,2); + numregressors = size(cppiregressors,2); + goodbeta = repmat([ones(1,numregressors) zeros(1,domotion*nummotion)],1,numrun); + index = 1; + for iB = 1:size(goodbeta,2) + if (goodbeta(iB) == 1) + cppi_grid{2,index}(iROI,:) = NaN*zeros(1,size(roiTC,2)); + cppi_grid{3,index}(iROI,:) = NaN*zeros(1,size(roiTC,2)); + if (parameters.cppi.StandardizeBetas) + cppi_grid{4,index}(iROI,:) = NaN*zeros(1,size(roiTC,2)); + end + index = index + 1; + end + end + result = 1; + mc_Logger('log',err.message); + end + if (result) + %[status result] = system(sprintf('rm -rf %s',model)); + [status(1) result] = system(sprintf('rm -rf %s',fullfile(model,'spmT*'))); + [status(2) result] = system(sprintf('rm -rf %s',fullfile(model,'con*'))); + [status(3) result] = system(sprintf('rm -rf %s',fullfile(model,'SPM*'))); + [status(4) result] = system(sprintf('rm -rf %s',fullfile(model,'mask*'))); + [status(5) result] = system(sprintf('rm -rf %s',fullfile(model,'beta*'))); + [status(6) result] = system(sprintf('rm -rf %s',fullfile(model,'ResMS*'))); + [status(7) result] = system(sprintf('rm -rf %s',fullfile(model,'RPV*'))); + if (any(status) ~= 0) + mc_Error('There was an error deleting temporary files: %s',result); + end + else + mc_Error('There was an error during cPPI calculation. Only 0s were returned.'); + end + + end + %now save cppi grid results + GridFilename = [parameters.Output.name '_cppi_grid']; + GridPath = mc_GenPath(fullfile(parameters.Output.directory,GridFilename)); + save(GridPath,'cppi_grid','-v7.3'); + otherwise + % + % Error case + % + SOM_LOG('FATAL ERROR : parameters.Output.type was not 0'); + return +end + +parameters.stopCPU.cppi = cputime; + +% Now write out the parameters to a file. + +paraName = fullfile(parameters.Output.directory,[parameters.Output.name '_parameters']); + +save(paraName,'parameters','SOM'); + +results = 'ok'; +return; diff --git a/cPPI/cppi_CreateImages.m b/cPPI/cppi_CreateImages.m new file mode 100644 index 00000000..2374f2b6 --- /dev/null +++ b/cPPI/cppi_CreateImages.m @@ -0,0 +1,26 @@ +function model = cppi_CreateImages(roiTC,parameters) +model = -1; +curpath = pwd; + +mc_GenPath(struct('Template',parameters.cppi.sandbox,'mode','makedir')); +cd(parameters.cppi.sandbox); + +Vtemplate.fname = fullfile(parameters.cppi.sandbox,'roiTC'); +Vtemplate.mat = spm_matrix([0 0 0 0 0 0 1 1 1 0 0 0]); +Vtemplate.dim = [parameters.rois.nroisRequested 1 1]; +Vtemplate.dt = [4 0]; +Vtemplate.descrip = ''; +%create dummy images consisting of roiTC + +FDTC = zeros(size(roiTC,2),1,1,size(roiTC,1)); +FDTC(:,1,1,:) = roiTC'; + +V(1:size(roiTC,1)) = Vtemplate; +for iV = 1:size(V,2) + V(iV).fname = sprintf('%s_%04d.img',V(iV).fname,iV); + spm_write_vol(V(iV),FDTC(:,:,:,iV)); +end + +Vmask = Vtemplate; +Vmask.fname = sprintf('%s_mask.img',Vtemplate.fname); +spm_write_vol(Vmask,ones(size(roiTC,2),1,1)); diff --git a/cPPI/cppi_CreateModel.m b/cPPI/cppi_CreateModel.m new file mode 100644 index 00000000..e394b314 --- /dev/null +++ b/cPPI/cppi_CreateModel.m @@ -0,0 +1,73 @@ +function model = cppi_CreateModel(cppiregressors,roiTC,parameters) +model = -1; +curpath = pwd; + +% mc_GenPath(struct('Template',parameters.cppi.sandbox,'mode','makedir')); +cd(parameters.cppi.sandbox); + +% Vtemplate.fname = fullfile(parameters.cppi.sandbox,'roiTC'); +% Vtemplate.mat = spm_matrix([0 0 0 0 0 0 1 1 1 0 0 0]); +% Vtemplate.dim = [parameters.rois.nroisRequested 1 1]; +% Vtemplate.dt = [4 0]; +% Vtemplate.descrip = ''; +% %create dummy images consisting of roiTC +% +% FDTC = zeros(size(roiTC,2),1,1,size(roiTC,1)); +% FDTC(:,1,1,:) = roiTC'; +% +% V(1:size(roiTC,1)) = Vtemplate; +% for iV = 1:size(V,2) +% V(iV).fname = sprintf('%s_%04d.img',V(iV).fname,iV); +% spm_write_vol(V(iV),FDTC(:,:,:,iV)); +% end +% +% Vmask = Vtemplate; +% Vmask.fname = sprintf('%s_mask.img',Vtemplate.fname); +% spm_write_vol(Vmask,ones(size(roiTC,2),1,1)); + +%enter these dummy images into an SPM model with the cppi regressors +offset = 0; +for iRun = 1:size(parameters.data.run,2) + SPM.Sess(iRun).U = []; + if (parameters.cppi.domotion) + SPM.Sess(iRun).C.C = [cppiregressors(offset+1:offset+parameters.data.run(iRun).nTimeAnalyzed,:) parameters.data.run(iRun).MotionParameters]; + else + SPM.Sess(iRun).C.C = [cppiregressors(offset+1:offset+parameters.data.run(iRun).nTimeAnalyzed,:)]; + end + SPM.Sess(iRun).C.name = repmat({'reg'},1,size(cppiregressors,2) + (parameters.cppi.domotion * size(parameters.data.run(iRun).MotionParameters,2))); + offset = offset + parameters.data.run(iRun).nTimeAnalyzed; +end +SPM.xY.P = spm_select('ExtFPList',parameters.cppi.sandbox,'roiTC_[0-9].*',[1:size(roiTC,1)]); +SPM.nscan = [parameters.data.run(:).nTIME]; +SPM.xBF.name = 'hrf'; +SPM.xBF.length = 32; +SPM.xBF.order = 1; +SPM.xBF.T = 16; +SPM.xBF.T0 = 1; +SPM.xBF.UNITS = 'secs'; +SPM.xBF.Volterra = 1; +SPM.xGX.iGXcalc = 'None'; +SPM.xX.K.HParam = 128; +SPM.xVi.form = 'AR(0.2)'; +SPM.xY.RT = parameters.TIME.run(1).TR; +%SPM.xM.VM = Vmask; +SPM.xM.VM = spm_vol(fullfile(parameters.cppi.sandbox,'roiTC_mask.img')); + +global defaults +defaults.mask.thresh = -Inf; +SPM = spm_fmri_spm_ui(SPM); +SPM = spm_spm(SPM); + +numbetas = size(SPM.xX.X,2); +SPM = rmfield(SPM,'xCon'); +for iB = 1:numbetas + contrast = [zeros(1,iB-1) 1 zeros(1,numbetas-iB)]; + SPM.xCon(iB) = spm_FcUtil('Set',sprintf('beta%d',iB),'T','c',contrast',SPM.xX.xKXs); +end +SPM = spm_contrasts(SPM); + +%return path to model when finished +model = pwd; +cd(curpath); + +return; diff --git a/cPPI/cppi_CreateRegressors.m b/cPPI/cppi_CreateRegressors.m new file mode 100644 index 00000000..163664e5 --- /dev/null +++ b/cPPI/cppi_CreateRegressors.m @@ -0,0 +1,155 @@ +function [regressors betanames] = cppi_CreateRegressors(roiTC,parameters) + +betanames = []; +load(parameters.cppi.SPM); +roiTC = SPM.xX.W*roiTC; %applying whitening matrix from SPM model +roiTC = spm_filter(SPM.xX.K,roiTC); %filter here with SPM filter, or use Robert's filter in preprocessing? + +RT = SPM.xY.RT; +dt = SPM.xBF.dt; +NT = RT/dt; +fMRI_T0 = SPM.xBF.T0; + +regressors = []; +offset = 0; +for iRun = 1:size(SPM.Sess,2) + roiTCtemp = roiTC(1+offset:parameters.data.run(iRun).nTimeAnalyzed+offset,:); + offset = offset + parameters.data.run(iRun).nTimeAnalyzed; + %calculate confounds + xY.X0 = SPM.xX.xKXs.X(:,[SPM.xX.iB SPM.xX.iG]); + xY.X0 = xY.X0(SPM.Sess(iRun).row,:); + xY.X0 = [xY.X0 SPM.xX.K(iRun).X0]; + xY.X0 = xY.X0(:,any(xY.X0)); +% +% [m n] = size(roiTCtemp); +% if m>n +% [v s v] = svd(roiTCtemp'*roiTCtemp); +% s = diag(s); +% v = v(:,1); +% u = roiTCtemp*v/sqrt(s(1)); +% else +% [u s u] = svd(roiTCtemp*roiTCtemp'); +% s = diag(s); +% u = u(:,1); +% v = roiTCtemp'*u/sqrt(s(1)); +% end +% d = sign(sum(v)); +% u = u*d; +% v = v*d; +% Y = u*sqrt(s(1)/n); +% roiTCtemp = Y; + + Sess = SPM.Sess(iRun); + + %condition names, and includes, and weights (not needed since we'll include all at weight 1) + U.name = {}; + U.u = []; + U.w = []; + + if (parameters.cppi.MaxConditions == 0) + u = length(Sess.U); + else + u = parameters.cppi.MaxConditions; + end + %%%%%%%%%%%%%%%%%%%REMOVE THIS LATER + %u = 2; %hardcoded to only use 2 conditions for MAS MSIT + for i = 1:u + for j = 1:length(Sess.U(i).name) + U.w = [U.w 1]; + U.u = [U.u Sess.U(i).u(33:end,j)]; + U.name{end+1} = Sess.U(i).name{j}; + end + end + + N = length(roiTCtemp); + k = 1:NT:N*NT; % microtime to scan time indices + + hrf = spm_hrf(dt); + + %create convolved explanatory {Hxb} variables in scan time + xb = spm_dctmtx(N*NT + 128,N); + Hxb = zeros(N,N); + for i = 1:N + Hx = conv(xb(:,i),hrf); + Hxb(:,i) = Hx(k+128); + end + xb = xb(129:end,:); + + %get confounds (in scan time) and constant term + X0 = xY.X0; + M = size(X0,2); + + Y = roiTCtemp; + + %remove confounds and save Y in output structure + Yc = Y-X0*inv(X0'*X0)*X0'*Y; + PMP.Y = Yc(:,1); + + %specify covariance components; assume neuronal response is white + %treating confounds as fixed effects + Q = speye(N,N)*N/trace(Hxb'*Hxb); + Q = blkdiag(Q,speye(M,M)*1e6); + + %get whitening matrix (NB: confounds have already been whitened) + W = SPM.xX.W(Sess.row,Sess.row); + + %create structure for spm_PEB + clear P + P{1}.X = [W*Hxb X0]; + P{1}.C = speye(N,N)/4; + P{2}.X = sparse(N+M,1); + P{2}.C = Q; + + % COMPUTE PSYCHOPHYSIOLOGIC INTERACTIONS + % use basis set in microtime + %--------------------------------------------------------------------- + % get parameter estimates and neural signal; beta (C) is in scan time + % This clever trick allows us to compute the betas in scan time which is + % much quicker than with the large microtime vectors. Then the betas + % are applied to a microtime basis set generating the correct neural + % activity to convolve with the psychological variable in mircrotime + %--------------------------------------------------------------------- + C = spm_PEB(Y,P); + xn = xb*C{2}.E(1:N); + xn = spm_detrend(xn); + + % setup psychological variable from inputs and contast weights (DON'T need weights, all 1) + %multiply psychological variables by neural signal + %convolve and resample at each scan for bold signal + %--------------------------------------------------------------------- + %PSY = zeros(N*NT,1); + j = size(U.u,2) + 1; + clear R; + for i = 1:size(U.u,2) + PSY{i} = full(U.u(:,i)); + PSYxn{i} = PSY{i}.*xn; + PSYHRF{i} = conv(PSY{i},hrf); + PSYHRF{i} = PSYHRF{i}((k-1)+fMRI_T0); + pmp{i} = conv(PSYxn{i},hrf); + pmp{i} = pmp{i}((k-1)+fMRI_T0); + pmp{i} = spm_detrend(pmp{i}); + R(:,i) = PSYHRF{i}; + R(:,j) = pmp{i}; + betanames2{i} = ['Run ' num2str(iRun) ' ' U.name{i}]; + betanames2{j} = ['Run ' num2str(iRun) ' ' U.name{i} ' x Seed interaction']; + j = j + 1; + end + + R(:,j) = PMP.Y; + betanames2{j} = ['Run ' num2str(iRun) ' Seed']; + %if (parameters.RegressFLAGS.Motion) + % R = [R parameters.data.run(iRun).MotionParameters]; + %end + betanames = [betanames betanames2]; + + %%%%% TEMPORARY HACK FOR GOLDIN ERT DATA %%%%%% + if (size(R,2)==13) + R = [zeros(size(R,1),4) R]; + end + %%%%% TEMPORARY HACK FOR GOLDIN ERT DATA %%%%%% + + regressors = [regressors; R]; +end + +return; + diff --git a/cPPI/cppi_CreateRegressors_spm.m b/cPPI/cppi_CreateRegressors_spm.m new file mode 100644 index 00000000..119fa15f --- /dev/null +++ b/cPPI/cppi_CreateRegressors_spm.m @@ -0,0 +1,38 @@ +function [regressors betanames] = cppi_CreateRegressors_spm(coords,parameters,roiTC) + +% clear jobs +% jobs{1}.stats{1}.results.spmmat = cellstr(parameters.cppi.SPM); +% jobs{1}.stats{1}.results.conspec(1).titlestr = 'EOI'; +% jobs{1}.stats{1}.results.conspec(1).contrasts = 1; +% jobs{1}.stats{1}.results.conspec(1).threshdesc = 'none'; +% jobs{1}.stats{1}.results.conspec(1).thresh = 1; +% jobs{1}.stats{1}.results.conspec(1).extent = 0; +% jobs{1}.stats{1}.results.print = 0; +% spm_jobman('run',jobs); +[a b c d] = fileparts(parameters.cppi.SPM); + +xSPM.swd = a; +xSPM.title = 'EOI'; +xSPM.Ic = 1; +xSPM.u = 0.99; +xSPM.k = 0; +xSPM.thresDesc = 'none'; +xSPM.Im = []; +[hReg,xSPM,SPM] = spm_results_ui('Setup',xSPM); + +xY.xyz = spm_mip_ui('SetCoords',coords); +xY.name = 'test'; +xY.Ic = 0; +xY.Sess = 1; +xY.def = 'sphere'; +xY.spec = 0; +%xSPM = evalin('base',xSPM); +%SPM = evalin('base',SPM); +%hReg = evalin('base',hReg); +[Y,xY] = spm_regions(xSPM,SPM,hReg,xY); + +PPI1 = spm_peb_ppi(parameters.cppi.SPM,'ppi',xY,[1 1 1],'task1',0); +PPI2 = spm_peb_ppi(parameters.cppi.SPM,'ppi',xY,[2 1 1],'task2',0); + +betanames = {'task1','task2','ppi1','ppi2','y'}; +regressors = [PPI1.P PPI2.P PPI1.ppi PPI2.ppi PPI1.Y]; \ No newline at end of file diff --git a/cPPI/cppi_Extract.m b/cPPI/cppi_Extract.m new file mode 100644 index 00000000..d2fdaa9c --- /dev/null +++ b/cPPI/cppi_Extract.m @@ -0,0 +1,76 @@ +function [cppi_grid result] = cppi_Extract(cppiregressors,model,parameters,cppi_grid,iROI,roiTC) +%cppi_grid - {B}[N x N] +% B = # of beta images +% N = # of ROIs + +%read beta images that were output by model +%save beta values into cell array grid +%need to skip/exclude betas from motion and run effects +result = 0; + +P = spm_select('FPList',model,'beta.*img'); +numimages = size(P,1); +Pt = spm_select('FPList',model,'spmT.*img'); +numt = size(Pt,1); + +load(fullfile(model,'SPM.mat')); +reg = []; +maxTP = 0; +for iRun = 1:size(SPM.Sess,2) + if (size(SPM.Sess(iRun).C.C,1) > maxTP) + maxTP = size(SPM.Sess(iRun).C.C,1); + end +end + +for iRun = 1:size(SPM.Sess,2) + add = []; + if (size(SPM.Sess(iRun).C.C,1) < maxTP) + add = NaN*zeros(maxTP-size(SPM.Sess(iRun).C.C,1),size(SPM.Sess(iRun).C.C,2)); + end + reg = [reg [SPM.Sess(iRun).C.C;add]]; +end + +%nummotion = size(parameters.data.run(1).MotionParameters,2); +%nummotion = size([parameters.data.run(:).MotionParameters],2); +[rr cc] = cellfun(@size,{parameters.data.run(:).MotionParameters},'UniformOutput',false); +nummotion = sum(cell2mat(cc)); +domotion = parameters.cppi.domotion; +numrun = size(parameters.data.run,2); +numregressors = size(cppiregressors,2); +%domotion = 1; +if (numimages ~= (numrun + (numrun*numregressors) + (domotion*nummotion))) + mc_Error('There are an inconsistent number of beta images and regressors.'); +end +if (numimages ~= numt) + mc_Error('There are an unequal number of beta images and T images'); +end + +goodbeta = []; +for iRun = 1:size(SPM.Sess,2) + goodbeta = [goodbeta ones(1,numregressors) zeros(1,domotion*size(parameters.data.run(iRun).MotionParameters,2))]; +end + +%goodbeta = repmat([ones(1,numregressors) zeros(1,domotion*nummotion)],1,numrun); +index = 1; +for iB = 1:size(goodbeta,2) + %extract beta iB and place in grid + %goodbeta(iB) = 1; + if (~goodbeta(iB)) + continue; + end + V = spm_vol(P(iB,:)); + Vt = spm_vol(Pt(iB,:)); + data = spm_read_vols(V); + datat = spm_read_vols(Vt); + cppi_grid{2,index}(iROI,:) = data'; + cppi_grid{3,index}(iROI,:) = datat'; + + if (parameters.cppi.StandardizeBetas) + temp = reg(:,iB); + sx = std(temp(~isnan(temp))); + sy = std(roiTC); + cppi_grid{4,index}(iROI,:) = data' .* (sx./sy); + end + index = index + 1; +end +result = 1; diff --git a/cPPI/cppi_batch_chunk.m b/cPPI/cppi_batch_chunk.m new file mode 100644 index 00000000..ad18b155 --- /dev/null +++ b/cPPI/cppi_batch_chunk.m @@ -0,0 +1,41 @@ +function results = cppi_batch_chunk(chunkFile) + spm_jobman('initcfg'); + spm_get_defaults('cmdline',true); + results = -1; + global mcLog; + load(chunkFile); + mc_Logger('log',sprintf('Current MATLAB PID: %d\n',feature('GetPID')),3); + SubjDir = tempSubjDir; + cwd = pwd; + for iSubject = 1:size(SubjDir,1) + clear D0 parameters results; + Subject=SubjDir{iSubject,1}; + + logstring = sprintf('%s: Now running subject %s\n',datestr(now),Subject); + mc_Logger('log',logstring,3); + + %load existing parameter file + OutputPath = mc_GenPath(OutputTemplate); + load(fullfile(OutputPath,ParameterFilename)); + clear global SOM; + global SOM; + SOM.silent = 1; + SOM_LOG('STATUS : 01'); + + [D0 parameters] = SOM_PreProcessData(parameters); + if D0 == -1 + SOM_LOG('FATAL ERROR : No data returned'); + mc_Error('There is something wrong with your template or your data. No data was returned from SOM_PreProcessData'); + else + results = cppi_ConnectomicPPI(D0,parameters); + if isnumeric(results) + SOM_LOG('FATAL ERROR : '); + mc_Error('There is something wrong with your template or your data. No results were returned from SOM_CalculateCorrelations'); + end + end + + logstring = sprintf('%s: Finished subject %s\n',datestr(now),Subject); + mc_Logger('log',logstring,3); + cd(cwd); + end +return; \ No newline at end of file diff --git a/cPPI/cppi_batch_mc_central.m b/cPPI/cppi_batch_mc_central.m new file mode 100644 index 00000000..6923780d --- /dev/null +++ b/cPPI/cppi_batch_mc_central.m @@ -0,0 +1,335 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% General calculations that apply to both Preprocessing and First Level +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Code to create logfile name +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +LogDirectory = mc_GenPath(struct('Template',LogTemplate,'mode','makedir')); +result = mc_Logger('setup',LogDirectory); +if (~result) + %error with setting up logging + mc_Error('There was an error creating your logfiles.\nDo you have permission to write to %s?',LogDirectory); +end + +global mcLog; +tempLog = mcLog; + +if (~exist('ROIOutput','var')) + ROIOutput = 'maps'; +end +if (~exist('Mode','var')) + Mode = 'full'; +end + + +RunMode = [0 0]; +if (strcmpi(Mode,'full')) + RunMode = [1 1]; +else + RunMode(1) = strcmpi(Mode,'parameters'); + RunMode(2) = strcmpi(Mode,'cppi'); +end + +spm('defaults','fmri'); +global defaults +warning off all + +spmver = spm('Ver'); +if (strcmp(spmver,'SPM8')==1) + spm_jobman('initcfg'); + spm_get_defaults('cmdline',true); +end + +if (RunMode(1) | sum(RunMode) == 0) + RunNamesTotal = RunDir; + NumScanTotal = NumScan; + + MaskBrain = 1; + + RegressGlobal = any(strfind(upper(RegressOrder),'G')); + RegressWhite = any(strfind(upper(RegressOrder),'W')); + RegressCSF = any(strfind(upper(RegressOrder),'C')); + DoBandpassFilter = any(strfind(upper(RegressOrder),'B')); + RegressMotion = any(strfind(upper(RegressOrder),'M')); + DoLinearDetrend = any(strfind(upper(RegressOrder),'D')); + + for iSubject = 1:size(SubjDir,1) + clear parameters; + clear global SOM; + + parameters.RegressFLAGS.prinComp = PrincipalComponents; + parameters.RegressFLAGS.global = RegressGlobal; + parameters.RegressFLAGS.csf = RegressCSF; + parameters.RegressFLAGS.white = RegressWhite; + parameters.RegressFLAGS.motion = RegressMotion; + parameters.RegressFLAGS.order = RegressOrder; + + Subject=SubjDir{iSubject,1}; + RunList=SubjDir{iSubject,3}; + + NumRun = size(RunList,2); + + TotalNumRun = size(NumScanTotal,2); %%% number of image runs if every run were present + + %%%%% This code cuts RunDir and NumScan based which Image Runs are present + NumScan=[]; + clear RunDir; + for iRun=1:NumRun + RunDir{iRun,1}=RunNamesTotal{RunList(1,iRun)}; + NumScan=horzcat(NumScan,NumScanTotal(1,RunList(1,iRun))); + end + + NumRun= size(NumScan,2); % number of runs + ImageNumRun=size(RunDir,1); %number of image folders + + GreyPath = mc_GenPath(GreyMatterTemplate); + WhitePath = mc_GenPath(WhiteMatterTemplate); + CSFPath = mc_GenPath(CSFTemplate); + BrainPath = mc_GenPath(BrainMaskTemplate); + + parameters.grey.File = GreyPath; + parameters.grey.ImgThreshold = GreyThreshold; + parameters.masks.white.File = WhitePath; + parameters.masks.csf.File = CSFPath; + parameters.masks.epi.File = BrainPath; + parameters.rois.mask.MaskFLAG = MaskBrain; + + for iRun = 1:NumRun + Run = RunDir{iRun}; + + ImagePath = mc_GenPath(ImageTemplate); + ImageFiles = spm_select('FPList',ImagePath, ['^' basefile '.*.' imagetype]); + RealignmentParametersFile = mc_GenPath(RealignmentParametersTemplate); + + parameters.data.run(iRun).P = ImageFiles; + if (size(SubjDir,2) > 3 && SubjDir{iSubject,4}(1) ~= 0) + parameters.data.run(iRun).nTIME = SubjDir{iSubject,4}(iRun); + else + parameters.data.run(iRun).nTIME = NumScan(iRun); + end + RealignmentParameters = load(RealignmentParametersFile); + RealignmentParametersDeriv = diff(RealignmentParameters); + + tempReg = RealignmentParameters; + tempRegD = []; + tempRegQ = []; + tempRegQD = []; + tempCSF = []; + tempCensor = []; + + if (IncludeMotionDeriv) + tempRegD = (diff([2*tempReg(1,:)-tempReg(2,:);tempReg])+diff([tempReg;2*tempReg(end,:)-tempReg(end-1,:)])) / 2; + %tempRegD = gradient(tempReg); %for vector this is equivalent to the above which is the average of forward and back derivative + end + if (IncludeMotionQuad) + tempRegQ = tempReg.^2; + end + if (IncludeMotionQuad && IncludeMotionDeriv) + tempRegQD = tempRegD.^2; + end + if (IncludeCSF) + tempCSF = load(mc_GenPath(CSFRegTemplate)); + end + if (IncludeCensor) + tempCensor = load(mc_GenPath(CensorTemplate)); + end + %RealignmentParametersDerivR = resample(RealignmentParametersDeriv,size(RealignmentParameters,1),size(RealignmentParametersDeriv,1)); + + parameters.data.run(iRun).MotionParameters = [RealignmentParameters tempRegD tempRegQ tempRegQD tempCSF tempCensor]; + parameters.data.run(iRun).MotionParameters = parameters.data.run(iRun).MotionParameters(1:parameters.data.run(iRun).nTIME,:); + + parameters.data.MaskFLAG = MaskBrain; + + parameters.TIME.run(iRun).TR = TR; + parameters.TIME.run(iRun).BandFLAG = DoBandpassFilter; + parameters.TIME.run(iRun).TrendFLAG = DoLinearDetrend; + parameters.TIME.run(iRun).LowF = LowFrequency; + parameters.TIME.run(iRun).HiF = HighFrequency; + parameters.TIME.run(iRun).gentle = Gentle; + parameters.TIME.run(iRun).padding = Padding; + parameters.TIME.run(iRun).whichFilter = BandpassFilter; + parameters.TIME.run(iRun).fraction = Fraction; + end + + Run = RunDir{1}; + ImagePath = mc_GenPath(ImageTemplate); + SOM_Mask = mc_GenPath(fullfile(ImagePath,'som_mask.img')); + + if (isempty(BrainMaskTemplate)) + parameters.rois.mask.File = SOM_Mask; + else + parameters.rois.mask.File = BrainPath; + end + output.Template = OutputTemplate; + output.type = 1; + output.mode = 'makedir'; + if (RunMode(1)) + OutputPath = mc_GenPath(output); + else + OutputPath = mc_GenPath(OutputTemplate); + end + + switch (ROIInput) + case 'files' + ROIFolder = mc_GenPath(ROITemplate); + for iROIs = 1:size(ROIImages,1) + ROI{iROIs} = fullfile(ROIFolder,ROIImages{iROIs}); + end + parameters.rois.files = char(ROI); + case 'directory' + ROIFolder = mc_GenPath(ROITemplate); + parameters.rois.files = spm_select('FPList',ROIFolder,'.*\.img|.*\.nii'); + + case 'coordinates' + parameters.rois.mni.coordinates = ROICenters; + if (iscell(ROISize)) + parameters.rois.mni.size = ROISize{1}; + else + XYZ = SOM_MakeSphereROI(ROISize); + parameters.rois.mni.size.XROI = XYZ(1,:); + parameters.rois.mni.size.YROI = XYZ(2,:); + parameters.rois.mni.size.ZROI = XYZ(3,:); + end + case 'grid' + ROIGridMask = mc_GenPath(ROIGridMaskTemplate); + ROIGridMaskHdr = spm_vol(ROIGridMask); + ROIGridBB = mc_GetBoundingBox(ROIGridMaskHdr); + grid_coord_cand = SOM_MakeGrid(ROIGridSpacing,ROIGridBB); + inOutIDX = SOM_roiPointsInMask(ROIGridMask,grid_coord_cand); + grid_coord = grid_coord_cand(inOutIDX,:); + parameters.rois.mni.coordinates = grid_coord; + if (iscell(ROIGridSize)) + parameters.rois.mni.size = ROIGridSize{1}; + else + XYZ = SOM_MakeSphereROI(ROIGridSize); + parameters.rois.mni.size.XROI = XYZ(1,:); + parameters.rois.mni.size.YROI = XYZ(2,:); + parameters.rois.mni.size.ZROI = XYZ(2,:); + end + case 'gridplus' + ROIGridMask = mc_GenPath(ROIGridMaskTemplate); + ROIGridMaskHdr = spm_vol(ROIGridMask); + ROIGridBB = mc_GetBoundingBox(ROIGridMaskHdr); + grid_coord_cand = SOM_MakeGrid(ROIGridSpacing,ROIGridBB); + inOutIDX = SOM_roiPointsInMask(ROIGridMask,grid_coord_cand); + grid_coord = grid_coord_cand(inOutIDX,:); + + grid_coord = [grid_coord; ROIGridCenters]; + + parameters.rois.mni.coordinates = grid_coord; + if (iscell(ROIGridSize)) + parameters.rois.mni.size = ROIGridSize{1}; + else + XYZ = SOM_MakeSphereROI(ROIGridSize); + parameters.rois.mni.size.XROI = XYZ(1,:); + parameters.rois.mni.size.YROI = XYZ(2,:); + parameters.rois.mni.size.ZROI = XYZ(2,:); + end + end + + parameters.Output.correlation = ROIOutput; + %parameters.Output.description = 'description of output'; + parameters.Output.directory = OutputPath; + parameters.Output.name = OutputName; + + parameters.cppi.SPM = mc_GenPath(fullfile(SPMTemplate,'SPM.mat')); + parameters.cppi.UseSandbox = UseSandbox; + parameters.cppi.NumScan = NumScan; + parameters.cppi.adjust = AdjustContrast; + + if (~exist('StandardizeBetas','var') || isempty(StandardizeBetas)) + StandardizeBetas = 1; + end + parameters.cppi.StandardizeBetas = StandardizeBetas; + if (~exist('MaxConditions','var') || isempty(MaxConditions)) + MaxConditions = 0; + end + parameters.cppi.MaxConditions = MaxConditions; + + if (UseSandbox) + [status hostname] = system('hostname -s'); + parameters.cppi.sandbox = fullfile([filesep hostname(1:end-1)],'sandbox','cppi',parameters.Output.directory); + else + parameters.cppi.sandbox = fullfile(parameters.Output.directory,'temp'); + end + parameters.cppi.domotion = IncludeMotion; + + ParameterFilename = [OutputName '_parameters']; + ParameterPath = mc_GenPath(fullfile(OutputPath,ParameterFilename)); + save(ParameterPath,'parameters'); + + end +end + +if (RunMode(2)) + %partition SubjDir into parameters.cppi.NumProcesses equal parts + NumSubj = size(SubjDir,1); + if (NumProcesses > 18) + NumProcesses = 18; + end + SubjPerChunk = floor(NumSubj / NumProcesses); + SubjRemain = mod(NumSubj,NumProcesses); + + for iChunk = 1:NumProcesses + %save each piece of SubjDir into a chunkN.mat file in temp location + offset = (iChunk - 1) * SubjPerChunk; + tempSubjDir = []; +% if (iChunk < NumProcesses) + for iSubject = 1+offset:SubjPerChunk+offset + tempSubjDir{end+1,1} = SubjDir{iSubject,1}; + tempSubjDir{end,2} = SubjDir{iSubject,2}; + tempSubjDir{end,3} = SubjDir{iSubject,3}; + if (size(SubjDir,2)>3) + tempSubjDir{end,4} = SubjDir{iSubject,4}; + end + + end +% else +% for iSubject = 1+offset:size(SubjDir,1) +% tempSubjDir{end+1,1} = SubjDir{iSubject,1}; +% tempSubjDir{end,2} = SubjDir{iSubject,2}; +% tempSubjDir{end,3} = SubjDir{iSubject,3}; +% tempSubjDir{end,4} = SubjDir{iSubject,4}; +% end +% end + if (SubjRemain > 0) + iSubject = NumSubj+1-SubjRemain; + tempSubjDir{end+1,1} = SubjDir{iSubject,1}; + tempSubjDir{end,2} = SubjDir{iSubject,2}; + tempSubjDir{end,3} = SubjDir{iSubject,3}; + if (size(SubjDir,2)>3) + tempSubjDir{end,4} = SubjDir{iSubject,4}; + end + + SubjRemain = SubjRemain - 1; + end + + [fd fn fe] = fileparts(mcLog); + mcLog = fullfile(fd,[fn '_chunk' num2str(iChunk) fe]); + chunkFile = fullfile(mc_GenPath(Exp),['chunk_' num2str(iChunk) '.mat']); + save(chunkFile,'tempSubjDir','OutputTemplate','Exp','OutputName','ParameterFilename','NumScan','mcLog'); + mcLog = tempLog; + end + %send a system call to start matlab, load a chunk file, and call + %cppi_batch_chunk with the loaded variables + + %spawn NumProcesses-1 other matlab processes + for iChunk = 1:NumProcesses-1 + chunkFile = fullfile(mc_GenPath(Exp),['chunk_' num2str(iChunk) '.mat']); + systemcall = sprintf('matlab -nosplash -nodesktop -r "addpath(fullfile(''%s'',''matlabScripts''));,addpath(fullfile(''%s'',''cPPI''));,addpath(fullfile(''%s'',''som''));,addpath(fullfile(''%s'',''SPM/SPM8/spm8_with_R4667''));,cppi_batch_chunk(''%s'');,quit;" &',mcRoot,mcRoot,mcRoot,mcRoot,chunkFile); + [status result] = system(systemcall); + end + %now the last one in this matlab. This one will always be equal to + %or greater in number of subjects to the previous ones. + iChunk = NumProcesses; + chunkFile = fullfile(mc_GenPath(Exp),['chunk_' num2str(iChunk) '.mat']); + cppi_batch_chunk(chunkFile); + + + +end + + + + diff --git a/cPPI/cppi_batch_mc_template.m b/cPPI/cppi_batch_mc_template.m new file mode 100644 index 00000000..307ced55 --- /dev/null +++ b/cPPI/cppi_batch_mc_template.m @@ -0,0 +1,362 @@ +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~% +%~~~~~~~~~~~~~~~~~~~~~~~~~~ Basic ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~% +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% GENERAL OPTIONS +%%% These options are shared among many of our scripts +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The folder that contains your subject folders +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Exp = '/net/data4/MAS'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Path where your logfiles will be stored +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +LogTemplate = '[Exp]/Logs/cPPI/'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Path where your images are located +%%% +%%% Variables you can use in your template are: +%%% Exp = path to your experiment directory +%%% iSubject = index for subject +%%% Subject = name of subject from SubjDir (using iSubject as index of row) +%%% iRun = index of run (listed in Column 3 of SubjDir) +%%% Run = name of run from RunDir (using iRun as index of row) +%%% * = wildcard (can only be placed in final part of template) +%%% Examples: +%%% ImageTemplate = '[Exp]/Subjects/[Subject]/func/run_0[iRun]/'; +%%% ImageTemplate = '[Exp]/Subjects/[Subject]/TASK/func/[Run]/' +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ImageTemplate = '[Exp]/Subjects/[Subject]/TASK/func/[Run]/'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% A list of run folders where the script can find functional images +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RunDir = { + 'run_05_lss'; + 'run_06_lss'; +}; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The list of subjects to process +%%% The format is 'subjectfolder',subject number in masterfile,[runs to include] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SubjDir = { +% '5001/Tx1',50011,[1 2], [225 240], 0 %%% this could be 225 and 240 +% '5001/Tx2',50012,[1 2], [215 235], 0 +% '5002/Tx1',50021,[1 2], [225 240], 0; +% '5002/Tx2',50022,[1 2], [225 240], 0; +% '5003/Tx1',50031,[1 2], [225 240], {'run_04/';'run_05/'}; +%%'5003/Tx2',50032,[1 2], 0, 0; %problem +%'5004/Tx1',50041,[1 2], 0, 0; +%'5004/Tx2',50042,[1 2], 0, 0; +%'5005/Tx1',50051,[1 2], 0, 0; +%'5005/Tx2',50052,[1 2], 0, 0; +%'5010/Tx1',50101,[1 2], 0, 0; +%'5010/Tx2',50102,[1 2], 0, 0; +%'5012/Tx1',50121,[1 2], 0, 0; +'5012/Tx2',50122,[1 2], 0, 0; +'5014/Tx1',50141,[1 2], 0, 0; +'5014/Tx2',50142,[1 2], 0, 0; +'5015/Tx1',50151,[1 2], 0, 0; +'5015/Tx2',50152,[1 2], 0, 0; +'5016/Tx1',50161,[1 2], 0, 0; +'5016/Tx2',50162,[1 2], 0, 0; +%'5017/Tx1',50171,[1 2], 0, 0; +%'5017/Tx2',50172,[1 2], 0, 0; +%'5018/Tx1',50181,[1 2], 0, 0; +%'5018/Tx2',50182,[1 2], 0, 0; +%'5019/Tx1',50191,[1 2], 0, 0; +%'5019/Tx2',50192,[1 2], 0, 0; +%'5020/Tx1',50201,[1 2], 0, 0; +'5020/Tx2',50202,[1 2], 0, 0; +'5021/Tx1',50211,[1 2], 0, 0; +'5021/Tx2',50212,[1 2], 0, 0; +%'5023/Tx1',50231,[1 2], 0, 0; +%'5023/Tx2',50232,[1 2], 0, 0; +% '5024/Tx1',50241,[1 2], 0, 0; +% '5024/Tx2',50242,[1 2], 0, 0; +% '5025/Tx1',50251,[1 2], 0, 0; +% '5025/Tx2',50252,[1 2], 0, 0; +% '5026/Tx1',50261,[1 2], 0, 0; +% '5026/Tx2',50262,[1 2], 0, 0; +% '5028/Tx1',50281,[1 2], 0, 0; +% '5028/Tx2',50282,[1 2], 0, 0; +% '5029/Tx1',50291,[1 2], 0, 0; +%'5029/Tx2',50292,[1 2], 0, 0; +%'5031/Tx1',50311,[1 2], 0, 0; +%'5031/Tx2',50312,[1 2], 0, 0; +'5032/Tx1',50321,[1 2], 0, 0; +'5032/Tx2',50322,[1 2], 0, 0; +'5034/Tx1',50341,[1 2], 0, 0; +'5034/Tx2',50342,[1 2], 0, 0; +%'5035/Tx1',50351,[1 2], 0, 0; +%'5035/Tx2',50352,[1 2], 0, 0; +%'5036/Tx1',50361,[1 2], 0, 0; +%'5036/Tx2',50362,[1 2], 0, 0; +%'5037/Tx1',50371,[1 2], 0, 0; +%'5037/Tx2',50372,[1 2], 0, 0; +%'5038/Tx1',50381,[1 2], 0, 0; +'5038/Tx2',50382,[1 2], 0, 0; +'5039/Tx1',50391,[1 2] , 0, 0; +'5039/Tx2',50392,[1 2], [220 235], {'run_03/';'run_04/'}; %%% unbalanced runs +'5040/Tx1',50401,[1 2], 0, 0; +'5040/Tx2',50402,[1 2], 0, 0; +'5041/Tx1',50411,[1 2], 0, 0; +'5041/Tx2',50412,[1 2], 0, 0; +'5042/Tx1',50421,[1 2], 0, 0; +'5042/Tx2',50422,[1 2], 0, 0; + }; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The TR your data was collected at +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +TR = 2; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The prefix of each functional file +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +basefile = 's8w3rarun'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Image Type should be either 'nii' or 'img' +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +imagetype = 'nii'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Number of Functional scans per run +%%% (if you have more than 1 run, there should be more than 1 value here) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +NumScan = [220 235]; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Original First Level Model location template +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SPMTemplate = '[Exp]/FirstLevel/[Subject]/MSIT/HRF/FixDur/Congruency_NORT_csf_mdq'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Include Realignment Parameters in your PPI model +%%% NOTE: generally this is only done if you included realignment +%%% parameters in your original first level model +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +IncludeMotion = 1; +IncludeMotionDeriv = 1; +IncludeMotionQuad = 1; +IncludeCSF = 1; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Path Template for realignment parameters file +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RealignmentParametersTemplate = '[Exp]/Subjects/[Subject]/TASK/func/[Run]/rp*.txt'; +CSFRegTemplate = '[Exp]/Subjects/[Subject]/TASK/func/[Run]/csf_5_comp.txt'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Analyze data on a local drive on the machine you run the script on +%%% If your data is located on /net/data4 or similar network drive, using +%%% this option will speed up the processing speed immensely. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +UseSandbox = 1; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% If you want to only use a subset of the conditions in your model, you +%%% can set this variable to that number of conditions. To look at all +%%% conditions this should be either empty or set to 0. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +MaxConditions = [2]; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The script will split the number of ROIs into this many seperate bins +%%% and run them simultaneously to speed up processing time. Be careful +%%% with setting this too high as it could cause the computer running the +%%% script to become unresponsive or crash. Please use the linux command +%%% top to investigate other processes running on the computer before +%%% starting your script. In general you should not set this higher than +%%% half of the available cores on a system. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +NumProcesses = 3; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Where to output the data +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +OutputTemplate = '[Exp]/FirstLevel/[Subject]/MSIT/HRF/FixDur/[OutputName]/'; +OutputName = 'Congruency_NORT_csf_mdq_cPPI'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Path and name of explicit mask to use at first level. +%%% Leaving this blank ('') will use a subject-specific mask +%%% NOTE: Subject-specific masks are not recommended for grid usage below. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +BrainMaskTemplate = '[Exp]/ROIS/rEPI_MASK_NOEYES.img'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Method of ROI Selection +%%% coordinates - provide the center of each seed and a radius +%%% files - provide a list of ROI files +%%% directory - provide a directory with ROI images. The analysis +%%% will use all images in that directory. +%%% grid - make a grid based on provided spacing and masked +%%% by provided mask +%%% gridplus - make a grid based on provided spacing and masked +%%% by provided mask. Additionally, add several ROIs +%%% based on a list of coordinates presented in +%%% ROIGridCenters +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROIInput = 'grid'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% If specifying ROI coordinates you need to provide a list of centers in +%%% MNI coordinates (mm) and a radius in voxels. +%%% NOTE: ROISize will be used as the radius of a sphere at each point. If +%%% you'd prefer to use the predefined 1,7,19, or 27 voxel sizes you will +%%% need to specify the size as a cell (i.e. {19}) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROICenters = [ + -22 0 -22; %left amyg Phan et al reappraisal + 22 0 -22; %right amyg Phan et al reappraisal + -10 14 -14; %left nucleus acumbens (Ref?) + 10 14 -14; %right nucleus acumbens (Ref?) + ]; +ROISize = {19}; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% If specifying ROI images you need to provide an ROI folder as well as a +%%% cell array list of ROI images. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROITemplate = '[Exp]/matlabScripts/ERT/ROIs/CC200/'; +ROIImages = { + }; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% If specifying ROI grid you need to provide a spacing and ROI size as +%%% well as an optional mask for grid point inclusion (a mask is strongly +%%% encouraged as not using one will return coordinates from across the entire +%%% bounding box). +%%% NOTE: ROIGridSize will be used as the radius of a sphere at each grid +%%% point. If you'd prefer to use the predefined 1,7,19, or 27 voxel sizes +%%% you will need to specify the size as a cell (i.e. {19}) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROIGridSpacing = 12; +ROIGridSize = {19}; +ROIGridMaskTemplate = '[Exp]/ROIS/rEPI_MASK_NOEYES.img'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The following option is only used if using 'gridplus' mode which adds +%%% additional hand-specified ROIs onto the calculated grid. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROIGridCenters = [ + -22 0 -22; %left amyg Phan et al reappraisal + 22 0 -22; %right amyg Phan et al reappraisal + -10 14 -14; %left nucleus acumbens (Ref?) + 10 14 -14; %right nucleus acumbens (Ref?) + ]; + + + + + + + +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~% +%~~~~~~~~~~~~~~~~~~~~~~~~~~~ Advanced ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~% +%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Paths to your anatomical images +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +GreyMatterTemplate = '[Exp]/ROIS/rEPI_MASK_NOEYES.img'; +WhiteMatterTemplate = '[Exp]/ROIS/rEPI_MASK_NOEYES.img'; +CSFTemplate = '[Exp]/ROIS/rEPI_MASK_NOEYES.img'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Constrain results to only regions in GreyMatterTemplate (1=yes, 0=no) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +MaskGrey = 0; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Value threshold to use for each mask. If left as [] use default 0.75 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +GreyThreshold = []; +WhiteThreshold = []; +CSFThreshold = []; +EPIThreshold = []; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% the order to perform the regressions etc +%%% D = detrend +%%% G = global +%%% W = white matter +%%% C = csf +%%% M = motion +%%% B = bandpass +%%% +%%% Suggested order is "DCWM" +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RegressOrder = ''; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Use this many principle components for regression +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +PrincipalComponents = 5; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Bandpass Filter Settings +%%% LowFrequency - low frequency cutoff +%%% HighFrequency - high frequency cutoff +%%% Gentle - 0 = no rolling, 1 = rolling +%%% Padding - number of timepoints to pad on beginning/end +%%% BandpassFilter - 0 = Matlab filter, 1 = SOM_Filter_FFT +%%% Fraction - fraction of variance for principle components +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +LowFrequency = 0.01; +HighFrequency = 0.1; +Gentle = 1; +Padding = 10; +BandpassFilter = 1; +Fraction = 1; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Mode to run som_batch_mc_central in +%%% 'test' = test script but do not save parameters or run any +%%% SOM code +%%% 'parameters' = run script and save parameters for each subject +%%% but do not run any SOM code +%%% 'cppi' = run SOM code on previously saved parameters +%%% 'full' = generate parameters and immediately run SOM code +%%% +%%% NOTE: If you choose mode 'cppi' then most variables except +%%% SubjDir and OutputTemplate/OutputName will be ignored as they +%%% will be loaded from the already existing parameter file for each +%%% subject. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Mode = 'full'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Type of output +%%% images - output R and Z images of correlation with each seed +%%% maps - output R and P matrix of correlations between seeds +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROIOutput = 'maps'; + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Do not edit below this line +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%DEVSTART +mcRoot = fullfile(fileparts(mfilename('fullpath')),'../'); +mcRoot = '/home/slab/users/mangstad/repos/cPPI/'; +%DEVSTOP + +%[DEVmcRootAssign] + +addpath(fullfile(mcRoot,'matlabScripts')); +addpath(fullfile(mcRoot,'cPPI')); +addpath(fullfile(mcRoot,'som')); +addpath(fullfile(mcRoot,'SPM','SPM8','spm8_with_R4667')); +addpath('/net/data4/MAS/Scripts/RissmanConnectivity/Variance/BOLD/'); + +cppi_batch_mc_central diff --git a/cPPI/cppi_matpreparation_template.m b/cPPI/cppi_matpreparation_template.m new file mode 100644 index 00000000..c8c7feed --- /dev/null +++ b/cPPI/cppi_matpreparation_template.m @@ -0,0 +1,126 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% GENERAL OPTIONS +%%% These options are the same between Preprocessing and First level +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The folder that contains your subject folders +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Exp = '/home/slab/mnt/psyche/net/data4/GO2010/PROJECTS/ERT'; + +ConnTemplate = '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/ERT_cPPI_norm_amyg_cppi_grid.mat'; + +OutputTemplate = { + '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Look_u/ERT_cPPI_norm_amyg_cppi_grid.mat'; + '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Maintain_u/ERT_cPPI_norm_amyg_cppi_grid.mat'; + '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Reappraise_u/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Look_s/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Maintain_s/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Reappraise_s/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/RvM_u/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/RvM_s/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/MvL_u/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/MvL_s/ERT_cPPI_norm_amyg_cppi_grid.mat'; +}; + +ConditionWeights = { + [2] [6 17] [1/2 1/2]; + [2] [7 18] [1/2 1/2]; + [2] [8 19] [1/2 1/2]; +% [4] [6 17] [1/2 1/2]; +% [4] [7 18] [1/2 1/2]; +% [4] [8 19] [1/2 1/2]; +% [2] [8 19 7 18] [1/2 1/2 -1/2 -1/2]; +% [4] [8 19 7 18] [1/2 1/2 -1/2 -1/2]; +% [2] [7 18 6 17] [1/2 1/2 -1/2 -1/2]; +% [4] [7 18 6 17] [1/2 1/2 -1/2 -1/2]; +}; + + +SubjDir = { +%%%'022',1,[1 2],0; %>3mm in both runs +'024',2,[1 2],0; +'046',3,[1 2],0; +'066',4,[1 2],0; +'074',5,[1 2],0; +%%%'076',6,[2],0; %>3mm in run 1 +'081',7,[1 2],0; +'084',8,[1 2],0; +'095',9,[1 2],0; +'103',10,[1 2],0; +'107',11,[1 2],0; +'110',12,[1 2],0; +'111',13,[1 2],0; +'124',14,[1 2],0; +'133',15,[1 2],0; +'137',16,[1 2],0; +'138',17,[1 2],0; +'142',18,[1 2],0; +'143',19,[1 2],0; +'148',20,[1 2],0; +'153',21,[1 2],0; +'157',22,[1 2],0; +'158',23,[1 2],0; +'160',24,[1 2],0; +'162',25,[1 2],0; +'167',26,[1 2],0; +%%%'173',27,[2],0; %>3mm in run 1, large artifact/mvt +'174',28,[1 2],0; +'176',29,[1 2],0; +'191',30,[1 2],0; +'200',31,[1 2],0; +'202',32,[1 2],0; +'203',33,[1 2],0; +'206',34,[1 2],0; +'207',35,[1 2],0; +'218',36,[1 2],0; +'219',37,[1 2],0; +'221',38,[1 2],0; +'227',39,[1 2],0; +'229',40,[1 2],0; +'233',41,[1 2],0; +'235',42,[1 2],0; +'237',43,[1 2],0; +'245',44,[1 2],0; +'251',45,[1 2],0; +'256',46,[1 2],0; +'268',47,[1 2],0; +'269',48,[1 2],0; +'293',49,[1 2],0; +'331',50,[1 2],0; +'332',51,[1 2],0; +'344',52,[1 2],0; +}; + +%DEVSTART +mcRoot = fullfile(fileparts(mfilename('fullpath')),'..'); +%DEVSTOP + +%[DEVmcRootAssign] + +addpath(genpath(fullfile(mcRoot,'matlabScripts'))); + +for iSubj = 1:size(SubjDir,1) + clear cppi_grid Subject; + + Subject = SubjDir{iSubj}; + + fprintf('%s\n',Subject) + load(mc_GenPath(ConnTemplate)); + + for iOut = 1:size(OutputTemplate,1) + clear rMatrix wp cprow include weights; + cprow = ConditionWeights{iOut,1}; + include = ConditionWeights{iOut,2}; + weights = ConditionWeights{iOut,3}; + for iCond = 1:size(include,2) + wp(:,:,iCond) = cppi_grid{cprow,include(iCond)} .* weights(iCond); + end + + rMatrix = sum(wp,3); + + mc_GenPath(struct('Template',OutputTemplate{iOut},'mode','makeparentdir')); + save(mc_GenPath(OutputTemplate{iOut}),'rMatrix'); + end +end \ No newline at end of file diff --git a/cPPI/test.m b/cPPI/test.m new file mode 100644 index 00000000..4ef7b719 --- /dev/null +++ b/cPPI/test.m @@ -0,0 +1,119 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% GENERAL OPTIONS +%%% These options are the same between Preprocessing and First level +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The folder that contains your subject folders +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Exp = '/home/slab/mnt/psyche/net/data4/GO2010/PROJECTS/ERT'; + +ConnTemplate = '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/ERT_cPPI_norm_amyg_cppi_grid.mat'; + +OutputTemplate = { + '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Look_u/ERT_cPPI_norm_amyg_cppi_grid.mat'; + '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Maintain_u/ERT_cPPI_norm_amyg_cppi_grid.mat'; + '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Reappraise_u/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Look_s/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Maintain_s/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/Reappraise_s/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/RvM_u/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/RvM_s/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/MvL_u/ERT_cPPI_norm_amyg_cppi_grid.mat'; +% '[Exp]/FirstLevel/[Subject]/ERT_cPPI_norm_amyg/MvL_s/ERT_cPPI_norm_amyg_cppi_grid.mat'; +}; + +ConditionWeights = { + [2] [6 17] [1/2 1/2]; + [2] [7 18] [1/2 1/2]; + [2] [8 19] [1/2 1/2]; +% [4] [6 17] [1/2 1/2]; +% [4] [7 18] [1/2 1/2]; +% [4] [8 19] [1/2 1/2]; +% [2] [8 19 7 18] [1/2 1/2 -1/2 -1/2]; +% [4] [8 19 7 18] [1/2 1/2 -1/2 -1/2]; +% [2] [7 18 6 17] [1/2 1/2 -1/2 -1/2]; +% [4] [7 18 6 17] [1/2 1/2 -1/2 -1/2]; +}; + + +SubjDir = { +%%%'022',1,[1 2],0; %>3mm in both runs +'024',2,[1 2],0; +'046',3,[1 2],0; +'066',4,[1 2],0; +'074',5,[1 2],0; +%%%'076',6,[2],0; %>3mm in run 1 +'081',7,[1 2],0; +'084',8,[1 2],0; +'095',9,[1 2],0; +'103',10,[1 2],0; +'107',11,[1 2],0; +'110',12,[1 2],0; +'111',13,[1 2],0; +'124',14,[1 2],0; +'133',15,[1 2],0; +'137',16,[1 2],0; +'138',17,[1 2],0; +'142',18,[1 2],0; +'143',19,[1 2],0; +'148',20,[1 2],0; +'153',21,[1 2],0; +'157',22,[1 2],0; +'158',23,[1 2],0; +'160',24,[1 2],0; +'162',25,[1 2],0; +'167',26,[1 2],0; +%%%'173',27,[2],0; %>3mm in run 1, large artifact/mvt +'174',28,[1 2],0; +'176',29,[1 2],0; +'191',30,[1 2],0; +'200',31,[1 2],0; +'202',32,[1 2],0; +'203',33,[1 2],0; +'206',34,[1 2],0; +'207',35,[1 2],0; +'218',36,[1 2],0; +'219',37,[1 2],0; +'221',38,[1 2],0; +'227',39,[1 2],0; +'229',40,[1 2],0; +'233',41,[1 2],0; +'235',42,[1 2],0; +'237',43,[1 2],0; +'245',44,[1 2],0; +'251',45,[1 2],0; +'256',46,[1 2],0; +'268',47,[1 2],0; +'269',48,[1 2],0; +'293',49,[1 2],0; +'331',50,[1 2],0; +'332',51,[1 2],0; +'344',52,[1 2],0; +}; + +%DEVSTART +mcRoot = fullfile(fileparts(mfilename('fullpath')),'..'); +%DEVSTOP + +%[DEVmcRootAssign] + +addpath(genpath(fullfile(mcRoot,'matlabScripts'))); + +for iSubj = 1:size(SubjDir,1) + clear cppi_grid Subject; + + Subject = SubjDir{iSubj}; + + fprintf('%s ',Subject) + load(mc_GenPath(ConnTemplate)); + + nan = 0; + for iCond = [6 7 8 17 18 19] + rMatrix = cppi_grid{2,iCond}; + rMatrix = rMatrix(1081:1084,1081:1084); + nan = nan + sum(isnan(rMatrix(:))); + end + fprintf(1,' %d\n',nan); +end \ No newline at end of file diff --git a/matlabScripts/mc_Logger.m b/matlabScripts/mc_Logger.m index 873c26ee..7d07dac1 100644 --- a/matlabScripts/mc_Logger.m +++ b/matlabScripts/mc_Logger.m @@ -76,7 +76,7 @@ shellcommand = sprintf('sed -i %s ''%s,%s s/^/%%/'' %s',macsedfix,num2str(1),num2str(lines),scriptcopy); [status r] = system(shellcommand); if (status ~= 0) - mc_Error(r); + %mc_Error(r); %remove comment later end mcLog = scriptlog; diff --git a/som/README.txt b/som/README.txt new file mode 100644 index 00000000..69b6a3e6 --- /dev/null +++ b/som/README.txt @@ -0,0 +1,22 @@ +# +# +# +# SOM code for doing two functionalities: +# +# +# 1) Self-Organizing Maps +# +# SOM_PreProcessData (needs updating to work with SOM_CalculateMaps) +# SOM_CalculateMaps +# +# 2) Seed based correlation maps/images +# +# SOM_PreProcessData +# SOM_CalculateMaps +# +# +# Use is only allowed with explicit permission of Robert C. Welsh +# +# rcwelsh@med.umich.edu +# + diff --git a/som/SOM_Alpha.m b/som/SOM_Alpha.m new file mode 100755 index 00000000..d4f0b353 --- /dev/null +++ b/som/SOM_Alpha.m @@ -0,0 +1,26 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% Ann Arbor MI. +% +% function results = SOM_Alpha(iter,nIter) +% +% Routine to return current value of the learning rate. +% +% You can change this to some other function. We use exponetial +% decay. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_Alpha(iter,nIter); + +global SOM + +results = SOM.alpha*exp(-iter/nIter/SOM.learningTimeConstant); + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_Bootstrap.m b/som/SOM_Bootstrap.m new file mode 100755 index 00000000..f0adf890 --- /dev/null +++ b/som/SOM_Bootstrap.m @@ -0,0 +1,126 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2006 +% +% +% A routine to mix up the phase of the individual data time-series +% for calculation of the false rate. +% +% function SOM_Bootstrap +% +% What goes in is the data, what comes out is the data +% with fourier components having their phases randomly +% assigned to -pi to pi. +% +% global SOMMem +% +% Input +% +% SOMMem{slot}.theData(nVoxel,nTime) - the data. +% SOMMem{slot}.fftData(nVoxe,nTime) - fft of the above data. +% SOMMem{slot}.PData(nVoxel,nTime) - phase of above data. +% SOMMem{slot}.afftData(nVoxel,nTime) - magniture of the above data. +% +% SOMMem{slot}.Padding - Size of padding. +% +% Output +% +% SOMMem{slot}.theDataRP - data with the phase of the frequency components +% randomly assigned. +% SOMMem{slot}.fftDataN - new fft of the data (fftDataN = fft(theDataRP,[],2); +% +% SOMMem{slot}.Dphase - the random phase used. +% +% Change to used global memory to help with out of memory issues. +% +% +% According to MRM 51:418-422 (2004) Laird, Rogers and Meyerand +% the phase should be randomly shifted simultaneously by the +% same amount for each frequency component. This will keep the +% spatial correlations. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function SOM_Bootstrap + +global SOMMem + +% Force to use slot #1 to grab the time course data +% However, we need to place the randomized data into slot 3. + +slot = 1; + +% Make sure that there is data present + +SOMMem{slot}.Error = 0; + +if isfield(SOMMem{slot},'theData') == 0 + fprintf('Error - no data present in the SOMMem global structure.\n'); + SOMMem{slot}.Error = 1; + return +end + +% Check that padding is there. + +if isfield(SOMMem{slot},'Padding') == 0 + SOMMem{slot}.Padding = 10; + fprintf('Forcing SOMMem{slot}.Padding = 10;\n'); +end + +SOMMem{slot}.Padding = floor(SOMMem{slot}.Padding); + +if SOMMem{slot}.Padding < 0 + SOMMem{slot}.Padding = 10; + fprintf('Forcing SOMMem{slot}.Padding = 10;\n'); +end + +% Get the fft of the data, save time by only doing once per looping call. +% You must CLEAR THIS IF YOU ARE DOING DIFFERENT DATA SET. + +if isfield(SOMMem{slot},'fftData') == 0 | ... + isfield(SOMMem{slot},'afftData') == 0 | ... + isfield(SOMMem{slot},'PData') == 0 | ... + any(size(SOMMem{slot}.fftData) - (size(SOMMem{slot}.theData)+[0 2*SOMMem{slot}.Padding])) + fprintf('Calculating Original Data Fourier Components.\n'); + % Calculate the padding. + Padding1 = mean(SOMMem{slot}.theData(:,1:SOMMem{slot}.Padding),[2]); + Padding2 = mean(SOMMem{slot}.theData(:,end-SOMMem{slot}.Padding+1:end),[2]); + SOMMem{slot}.fftData = fft([repmat(Padding1,[1 SOMMem{slot}.Padding]) SOMMem{slot}.theData repmat(Padding2,[1 SOMMem{slot}.Padding])],[],2); + % Get the magnitude of the data. + SOMMem{slot}.afftData = abs(SOMMem{slot}.fftData); + % Get the phase of the data. + SOMMem{slot}.PData = angle(SOMMem{slot}.fftData); +else + fprintf('Using Pre-existing FFT of ''theData''.\n'); +end + +% How big is our sample. + +N = size(SOMMem{slot}.fftData,2); + +Npnts2 = floor((N-1)/2); + +% Determine even/oddness of number of time points. +OddEven = 1-mod(N,2); + +% randomize the new phase change. +dphase = pi-2*pi*rand(1,Npnts2); + +SOMMem{slot}.Dphase = repmat([0 -fliplr(dphase) pi-2*pi*rand(1,OddEven) dphase],[size(SOMMem{slot}.theData,1) 1]); + +% Reassemble the data. +SOMMem{slot}.fftDataN = SOMMem{slot}.afftData.*exp((SOMMem{slot}.PData+SOMMem{slot}.Dphase)*i); + +% Calculate the new signal. Taking the real part is fine as the original +% signal is real. + +SOMMem{slot}.DataN = real(ifft(SOMMem{slot}.fftDataN,[],2)); + +% And unit norm it at the same time. + +SOMMem{3}.theData = SOM_UnitNormMatrix(SOMMem{slot}.DataN(:,1+SOMMem{slot}.Padding:end-SOMMem{slot}.Padding),2); + +% +% All done. +% diff --git a/som/SOM_BuildMap.m b/som/SOM_BuildMap.m new file mode 100755 index 00000000..a4512082 --- /dev/null +++ b/som/SOM_BuildMap.m @@ -0,0 +1,37 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2007 +% +% A routine to build a map out of a SOM element. +% +% You need to pass the cost function and which element +% +% global SOMMem +% +% SOMMem{slot}.theData(nVoxel,nTime); +% SelfOMap(nTime,nSOM); +% +% SOMMem{1}.maskInfo.hdr is necessary +% +% This is a kludge at the moment. +% +% function CFMap = SOM_BuildMap(costFunction,whichElement) +% +% Utilized SOMMem and uses memory slot #1 +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function CFMap = SOM_BuildMap(costFunction,whichElement) + +global SOMMem + +CFMap = zeros(SOMMem{1}.maskInfo.size); + +CFMap(SOMMem{1}.maskInfo.iMask) = costFunction(:,whichElement); + +return + +% +% All done +% diff --git a/som/SOM_BuildROILinearIDX.m b/som/SOM_BuildROILinearIDX.m new file mode 100755 index 00000000..bdd8ab47 --- /dev/null +++ b/som/SOM_BuildROILinearIDX.m @@ -0,0 +1,114 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Build array of linear indices. +% +% This routine should only be called from SOM_CalculateCorrelations +% +% +% INPUT +% +% parameters -- see SOM_PreProcessData and SOM_CalculateCorrelations +% +% OUTPUT +% +% rois -- with full linear indices. +% +% function rois = SOM_BuildROILinearIDX(parameters) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function rois = SOM_BuildROILinearIDX(parameters) + +% +% Read in the mask of the brain +% + +rois = parameters.rois; + +MVol = spm_read_vols(parameters.maskInfo.header); + +% Rebinerize it. + +MVol = MVol > 0; + +% +% Load the ROI voxel indices for the calculation. +% + +rois.IDX = {}; + +for iROI = 1:rois.nroisRequested + + if rois.ROIOK(iROI) + + switch rois.type + + % + % this case is mni coordinates + % + + case 0 + + + % Get the MNI coordinates of the middle of this ROI. + + ROICoordsMM = rois.mni.coordinates(iROI,:); + + % Convert to voxel indices. + ROICoordsVoxel = round((inv(parameters.maskHdr.mat)*([ROICoordsMM 1]'))); + + % Build the actual ROI into an array of indices + VOXELIDX = [ + rois.mni.size.XROI+ROICoordsVoxel(1) + rois.mni.size.YROI+ROICoordsVoxel(2) + rois.mni.size.ZROI+ROICoordsVoxel(3)] ; + + % Convert back to MNI + VOXELMM = parameters.maskHdr.mat*([VOXELIDX;ones(1,size(VOXELIDX,2))]); + + % Now check the ones that are inside the mask, we have to do + % this in the event that the ROI has expanded past the center + % to be outside of the image. + + THISROI_inIDX = round(SOM_roiPointsInMask(parameters.maskInfo.header.fname,(VOXELMM(1:3,:))')); + + % Convert to linear indices + rois.IDX{iROI} = sub2ind(parameters.maskHdr.dim(1:3),VOXELIDX(1,THISROI_inIDX),VOXELIDX(2,THISROI_inIDX),VOXELIDX(3,THISROI_inIDX)); + + % Recount how many voxels survived the masking in this ROI. + rois.nvoxels(iROI) = length(rois.IDX{iROI}); + case 1 + + % Read in the image and "AND" it with the ROI + ROIVOL = spm_read_vols(rois.hdr(iROI)); + ROIVOL = ROIVOL.*MVol; + + % Determine linear indices + rois.IDX{iROI} = find(ROIVOL); + + % Recount how many voxels survived the masking in this ROI. + rois.nvoxels(iROI) = length(rois.IDX{iROI}); + + end + + % + % Now find out where these exist in the data stream + % + + rois.IDX{iROI} = SOM_ROIIDXnMASK(parameters,rois.IDX{iROI}); + + % Redetermine if this ROI really survived masking etc. + rois.ROIOK(iROI) = (length(rois.IDX{iROI}) > 0); + end +end + +return + +% +% All done +% diff --git a/som/SOM_Ca_back.m b/som/SOM_Ca_back.m new file mode 100755 index 00000000..d95d37bf --- /dev/null +++ b/som/SOM_Ca_back.m @@ -0,0 +1,457 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% function results = SOM_CalculateMap(theData,nSOM,nIter,[SelfOMap],[slot]) +% +% theData = theData(nSpace,nTime); +% nSOM = number of basis functions in the SOM +% nIter = number of iterations. +% slot = memory slot to use. 1 = time series data (default) +% 2 = super cluster use. +% 3 = boostrap probabilities. +% +% NOTE : The "slot" is used to help with memory management. +% Self-Organzing Map calculations are very memory +% intensive, so we are using global memory. However, +% when calculating the supercluster map it would be useful +% to not wipe out the data, so we have two slots +% available for memory. +% +% Please use them wisely. +% +% You will break the calculation if you place +% your time-series data in slot #2! +% +% Hopefully you can avoid "Out of memory" error by +% specifying slot 1 for the big time series matrix, and +% slot 2 for super cluster calculation. +% +% Between subjects you may need to execute the "pack" +% command. +% +% Worst case, trim you session, save appropriate +% things to disk and restart matlab. +% +% +% results = results structure, it will at least contain +% the resulting SOM. +% +% results = results.SOM (the map) +% results.IDX (for each space element which SOM it is related to +% best) +% results.WT (how much of the variance is explained by that) +% +% this is a work in progress! +% +% Currently the cost functionm is Cos(Theat) = U dot V +% where U and V have unit length. +% +% You can set the SOM parameters with +% +% global SOM +% +% SOM.sigma = Initial neighborhood size. +% SOM.sigmaTimeConstant = Rate the neighborhood shrinks. +% SOM.learningTimeConstant = Rate that learning changes. +% SOM.alpha = Initial learning rate +% SOM.Cost = Cost function. (0=U.V, +% 1=|U-V|, 2=|U-V|^2, 3=M.I.). +% NOTE U.V is fastest metric. +% SOM.nPnts = number of points in histogram range : 0 - 1[2]. +% NOTE used by SOM_CalcP. (2 is upper range for |U-V| +% cost, a guess. See "SOM_CalcP.m") +% SOM.saveHistory = History level to save. (0/1/2); +% 0 = none; +% 1 = some; +% 2 = full - memory intensive. +% SOM.WeightOption 0 = no weighting of components. +% 1 = weight by variance +% 2 = weight by st-dev +% +% If you don't specify these parameters they will assume default +% values of: +% +% SOM.alpha = .1 (See "SOM_Alpha.m" on evolution) +% SOM.learningTimeConstant = 2; +% SOM.sigmaTimeConstant = .25; +% SOM.sigma = nSOM/2; guess? +% SOM.Cost = 0; (Opening angle cost function.) +% SON.nPnts = 1000; +% SOM.saveHistory = 1; - Save most of history, but +% not full weights. +% SOM.WeightOption = 0; +% +% Alpha (SOM_Alpha.m) changes according to: +% +% exp(-iter/(nIter * SOM.learningTimeConstant)); +% +% Neighborhood size changes according to: +% +% exp(-iter/(nIter * SOM.sigmaTimeConstant)); +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_CalculateMap(theData,nIter,v,SelfOMap,slot) + +global time; +global SOM +global SOMMem +%SOM.silent = true; +%--------------------------Ben added 6/28/07-------------------- +%[b,v] = sphere_iter(1,1); +nSOM = length(v); + +% Keep track of the version. + +MajorVersion = 1; +MinorVersion = 001; + +SOM.Version = sprintf('%d.%03d',MajorVersion,MinorVersion); + +% Keep track of the time. + +results.StartTime=cputime; + +tic; + +% Which method to determine neighborhood (same code, just cleaned +% up?). + +if ~isfield(SOM,'OldMethod') + SOM.OldMethod = 0; +end + +% Check to see which memory slot + +if exist('slot') == 0 + slot = 1; + if ~SOM.silent + fprintf('Defaulting to use memory slot 1.\n'); + end +end + +if slot ~= 1 & slot ~= 2 + if ~SOM.silent + fprintf('Forcing to use slot #1.\n'); + end + slot = 1; +end + +if ~SOM.silent + fprintf('Using memory slot #%d\n',slot); +end + +SOM.slot = slot; + +% Check to see if the necessary fields exist. + +% The neighborhood size +if ~isfield(SOM,'sigma') + SOM.sigma = sqrt(nSOM)/2; + if ~SOM.silent + fprintf('SOM.sigma -> %f\n',SOM.sigma); + end +end + +% How quickly to modify the neighborhood size. +if ~isfield(SOM,'sigmaTimeConstant') + SOM.sigmaTimeConstant = 1/4; + if ~SOM.silent + fprintf('SOM.sigmaTimeConstant -> %f\n',SOM.sigmaTimeConstant); + end +end + +% How quickly to modify the map learning rate. +if ~isfield(SOM,'learningTimeConstant') + SOM.learningTimeConstant = 2; + if ~SOM.silent + fprintf('SOM.learningTimeConstant -> %f\n',SOM.learningTimeConstant); + end +end + +% The initial map learning rate. +if ~isfield(SOM,'alpha') + SOM.alpha = .1; + if ~SOM.silent + fprintf('SOM.alpha -> %f\n',SOM.alpha); + end +end + +% The cost function +if ~isfield(SOM,'Cost') + SOM.Cost = 0; + if ~SOM.silent + fprintf('SOM.Cost -> %f\n',SOM.Cost); + end +end + +% The saving history. +if ~isfield(SOM,'saveHistory') + SOM.saveHistory = 1; + if ~SOM.silent + fprintf('SOM.saveHistory -> %f\n',SOM.saveHistory); + end +end + +% The saving history. +if ~isfield(SOM,'WeightOption') + SOM.WeightOption = 0; + if ~SOM.silent + fprintf('SOM.WeightOption -> %f\n',SOM.WeightOption); + end +end + +% Make sure that the Weight Option is Valid. +SOM.WeightOption = SOM.WeightOption(1); + +if sum(ismember([0 1 2],SOM.WeightOption)) == 0 + SOM.WeightOption = 0; + if ~SOM.silent + fprintf('SOM.WeightOption -> %f\n',SOM.WeightOption); + end +end + +% Determine the size of the space we are dealing with. + +%if sqrt(nSOM)~=floor(sqrt(nSOM)) +% fprintf('Can only do square SOM!\n'); +% fprintf('Forcing SOM to be square\n'); +% nSOM = floor(sqrt(nSOM)+.5)^2; +% fprintf('New size is : %d\n',nSOM); +%end + +% Record the size of the SOM. +SOM.nSOM = nSOM; +SOM.nIter = nIter; + +nGrid = sqrt(nSOM); +SOM.nGrid = nGrid; + +nSpace = size(theData,1); +nTime = size(theData,2); + +if ~SOM.silent +fprintf('Number of time points : %d\n',nTime); +fprintf('Number of space points : %d\n',nSpace); +end + +% Randomize our initial SOM and normalize the basis. +% +% Modified on 8/23/2005 to use normaly distributed +% elements of vector. This will make it uniformly +% distributed on a sphere in n-space. +% +% Found by doing google search and getting +% argument of Steve Rayhawk at Harvey Mudd. +% Seems to work by graphical validation - still need +% to find the paper for citation. See reference in +% Donald Knuth book: The Art of Computer Programming, vol2 +% Seminumerical Algorithms, Addison-Wesley, 1969. +% + +if exist('SelfOMap') == 1 & ~any([nTime nSOM]-size(SelfOMap)) + if ~SOM.silent + fprintf('Using passed initialized SelfOMap\n'); + end +else + if ~SOM.silent + fprintf('Randomizing SelfOMap\n'); + end + SelfOMap = randn(nTime,nSOM); + SelfOMap = SOM_UnitNormMatrix(SelfOMap,1); +end + +% Store a copy of the initial one. +iSelfOMap = SelfOMap; + +clear SOM_UnitNormMatrix; + +% Unit norm the data. We are not necessarily interested in effect +% size at the moment versus explanatory basis. We can +% get the "beta's" later! + +% This is not the best, as when SOM_SuperClusterEasy calls us, we globber +% what was in here. Not good! 2007-03-23. +% Come up with a mex solution? + +SOMMem{slot}.theData = SOM_UnitNormMatrix(theData,2); + +% Calculate the Variance for weights. +SOMMem{slot}.varData = var(SOMMem{slot}.theData,[],2); + +% Weights for biasing the SOM to voxels that have high variance but +% low population number. + +switch SOM.WeightOption + case 0 + SOMMem{slot}.Weights = ones(size(theData,1),1); + case 1 + SOMMem{slot}.Weights = var(SOMMem{slot}.theData,[],2); + case 2 + SOMMem{slot}.Weights = std(SOMMem{slot}.theData,[],2); + case default + SOMMem{slot}.Weights = ones(size(theData,1),1); +end +SOMMem{slot}.Weights = repmat(SOMMem{slot}.Weights,[1 size(SOMMem{slot}.theData,2)]); +clear theData + +clear SOM_UnitNormMatrix; + +% Ok, let's go iterate the map. At the moment +% we'll just stop the calculation after a sufficient number +% iterations. + +iter = 0; + +%neighborDist = SOM_NeighborDist(nGrid); + +%lneighborDist = reshape(neighborDist,[nGrid nGrid nGrid*nGrid]); +%--------------------------Ben added 6/12/07-------------------- + + +lneighborDist = zeros(nSOM,nSOM); +for ii=1:nSOM + for jj=1:nSOM + lneighborDist(ii,jj) = abs(acos(dot(v(ii,:),v(jj,:)))*2); + end +end + +history = {}; + +pV_init = (SOMMem{slot}.theData.*SOMMem{slot}.Weights)'; +if ~SOM.silent +fprintf('iter,alpha,NeighSigma,cpu\n'); +end + +%mov = avifile('animated4.avi','compression','Cinepak') + +while iter < nIter + tic; + % Increment the iteration + iter = iter + 1; + % Learning function = g(time); + alpha = SOM_Alpha(iter,nIter); + % Determine the neighbor map. + % For each element of the SOM, there will be an associated + % neighborhood. The call should return the indices of these + % SOM vectors as well as the distance for use in the weight + % calculation. + NeighSigma = SOM.sigma*exp(-iter/nIter/SOM.sigmaTimeConstant); + if SOM.OldMethod == 0 + SOMNeighborMap = exp(-lneighborDist.^2/NeighSigma^2); + %keyboard; + else + SOMNeighborMap = SOM_NeighborMap([nGrid nGrid],NeighSigma); + end + + % Determine the SOM vectors the data are closest to and save the + % history. + [idx wts] = SOM_FindClosest(SelfOMap,slot); + if SOM.saveHistory > 0 + history{iter}.idx = idx; + history{iter}.wts = wts; + history{iter}.SelfOMap = SelfOMap; + history{iter}.sigma = NeighSigma; + history{iter}.alpha = alpha; + %history{iter}.neighMap = SOMNeighborMap(:,:,(nGrid-1)*floor(nGrid/2)); + if SOM.saveHistory > 1 + history{iter}.dataBySOM = SOMMem{slot}.dataBySOM; + end + history{iter}.cpuTime = cputime; + end + % + clear SOM_FindClosest; + % Now calculate the perturbation to the SOM. + dSelfOMap = 0*SelfOMap; + + nullSOM = 0*SelfOMap; + for iSOM = 1:nSOM + % Deterimine which data affect this SOM vector. + dI = find(idx==iSOM); + if length(dI) > 0 + % pertube all the same regardless of number of perturbers. + % + % Modified to allow heavier weighting based on variance of + % the time-series vector for a voxel. This is under the + % assumption that we want variance explained by the model. + % + % This also makes the assumption that signal is high + % variance? + % + % 2007.03.20 Robert C. Welsh + % + pV = sum(pV_init(:,dI),2); + %pV = sum(pV_init(:,dI),2)/sum(SOMMem{slot}.Weights(dI,1)); + %pV = SOM_UnitNormMatrix(pV,1); + %-------------------Ben changed 6/13/07----------------------- + if SOM.OldMethod == 0 + vlx = pV*reshape(SOMNeighborMap(:,iSOM),[1 nSOM]); + dSelfOMap = dSelfOMap + vlx; + else + dSelfOMap = dSelfOMap + SOM_ModBasis(pV,nullSOM, ... + SOMNeighborMap{iSOM},iter,nIter); + end + clear SOM_ModBasis; + end + end + % Update the SOM. + %dSelfOMap = dSelfOMap/(nSpace/nSOM); + SelfOMap = SelfOMap + alpha*dSelfOMap; + SelfOMap = SOM_UnitNormMatrix(SelfOMap,1); + if(any(any(isnan(SelfOMap)))) +% keyboard + end + clear SOM_UnitNormMatrix; + xx=toc; + if SOM.saveHistory > 0 + history{iter}.toc = xx; + end + if ~SOM.silent + fprintf('%03d %f %f %f\n',iter,alpha,NeighSigma,xx); + end +end +SelfOMap = SOM_UnitNormMatrix(SelfOMap,1); +%mov = close(mov); + +% All done, and now pack up the results to be sent back to the calling +% function. + +results.SelfOMap = SelfOMap; + +[results.IDX results.WTS results.DIDX results.DWTS] = SOM_FindClosest(SelfOMap,slot); + +clear SOM_FindClosest; + +% Final cost function evaluation. + +results.dataBySOM = SOMMem{slot}.dataBySOM; + +% Return a copy of the initial one used. + +results.iSelfOMap = iSelfOMap; +results.history = history; + +results.weights = []; +for iter = 1:nIter + results.weights = [results.weights sum(results.history{iter}.wts)]; +end + +% Record the configuation. + +results.SOM = SOM; +results.H = hist(results.IDX,[0:results.SOM.nSOM]); + +clear history; +clear SelfOMap; + +% Store the finish time. + +results.StopTime=cputime; + +return + +% +% All done. +% diff --git a/som/SOM_CalcInOutComp.m b/som/SOM_CalcInOutComp.m new file mode 100755 index 00000000..f9c71b3e --- /dev/null +++ b/som/SOM_CalcInOutComp.m @@ -0,0 +1,53 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2010 +% +% Loop on the components passed and calculate the in/out ratio +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_CalcInOutComp(DataBySOM,pVal,TheTemplate) + +global SOMMem + +results = []; + +if isfield(SOMMem{1},'maskInfo') == 0 + return +end + +maskDim = size(TheTemplate); + +if sum(any(size(TheTemplate) - SOMMem{1}.maskInfo.size)) ~= 0 + fprintf('Masking template doesn''t match the size of the original data.\n'); + return +end + +BrainMask = zeros(SOMMem{1}.maskInfo.size); + +BrainMask(SOMMem{1}.maskInfo.iMask) = 1; + +NTheTemplate = TheTemplate .* BrainMask; + +ROIRemoved = sum(TheTemplate(:)-NTheTemplate(:)); + +NotTemplate = BrainMask.*(1 - NTheTemplate); + +rCutOff = SOM_CalcRCutoff(DataBySOM,pVal); + +results = []; + +BinConnMap = 0*BrainMask; + +for iEXP = 1:size(DataBySOM,2) + BinConnMap = SOM_BuildMap(DataBySOM,iEXP)>=rCutOff(iEXP,2); + results = [results ; SOM_InOutComponent(BinConnMap,TheTemplate)]; +end + +return + +% +% All done. +% + \ No newline at end of file diff --git a/som/SOM_CalcP.m b/som/SOM_CalcP.m new file mode 100755 index 00000000..59caf44f --- /dev/null +++ b/som/SOM_CalcP.m @@ -0,0 +1,262 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2006-2007 +% +% +% A routine to calculate the probability "p" for the +% voxel to be associated with a random examplar. +% +% This uses the metric : U.V +% +% function [results] = SOM_CalcP(SelfOMap,[N]) +% +% +% global SOMMem +% +% SOMMem.theData - time courses (space,time) +% +% Input: +% SelfOMap - self-organizing map (time,exemplar#) +% +% Option: +% |N| - number of times to run +% bootstrap calculation. +% +% Sign(N) - + = include all voxels in calculation. +% - - = exclude assosciated voxels from null. +% dist. +% global SOM +% +% SOM.nPnts - number of points in histogram range : 0 - 1. +% +% NOTE : Other code uses the SOM global as well. +% +% Output: +% results.dataBySOMNull - weights. +% results.SOMHist - histograms of null dist. +% results.SOMCDFH - tail cdf. 0 = very end of dist. +% results.pVals - if < 0 then anti-correlated. +% +% Probability calcuted is the probability of making observation >= +% U.V or some other cost function. +% +% +% Modified on 2007-03-18 +% +% Using SOMMem global memory to avoid out of memory errors? +% +% Also made some changes in the histogram so that we don't store +% huge cost metric array. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results] = SOM_CalcP(SelfOMap,varargin) + +global SOM + +global SOMMem + +global SOMGaussian + +% Force to use slot #1 + +slot = 1; + +% However, bootstrap returns data into slot 3. + +% We need to lookup the SOM associated with a given voxel. + +[results.IDX results.WTS results.DIDX results.DWTS] = SOM_FindClosest(SelfOMap,1); + +% Have 1001 bins by default. Should this +% be increased for resolution of the p-value? + +nPnts = 1000; + +% Hard code limits on the histograms to build for PDF'.s This is +% not good, but we need to have something? Fix later. + +histUpperLimits = [1 2 4 2]; + +% Did the user set the number of points to use in histograms? + +if ~isfield(SOM,'nPnts') + SOM.nPnts = nPnts; +end + +% Grab the number of points used in the histogram. + +nPnts = SOM.nPnts; + +% Size of the Self Organizing Map. + +nSOM = size(SelfOMap,2); + +% Check for the N option, that is the number of times to run the +% null distribution. + +N = 1; + +if nargin >= 2 + if isnumeric(varargin{1}) + N = varargin{1}(1); + end +end + +N = floor(N); + +if N < 0 + fprintf('Excluding associated voxels from null calculation.\n'); +end + +results.N = N; + +fprintf('Running %d iteration(s) of the null distributions.\n',N); + +% Do this just in case. + +SelfOMapN = SOM_UnitNormMatrix(SelfOMap,1); + +clear SOM_UnitNormMatrix; + +% Now calculate the null hypothesis distribution. +% and then calculate the new dataBySOM for nulls. Question is how many times to +% do this? Assuming that our data is on order of 30,000 voxels, then +% once should be enough? + +%results.dataBySOMNull = zeros(size(SOMMem{3}.theData,1),size(SelfOMap,2)); + +% Get the upper limit of the histograms to accumulate. + +histUPPER = histUpperLimits(SOM.Cost+1); + +% Store original data. + +results.SOMHist = zeros(nSOM,nPnts+1); +results.SOMCDFH = zeros(nSOM,nPnts+1); + +results.NAssoc = zeros(nSOM,1); + +for iNull = 1:abs(N) % Run 'N' null distributions? + SOM_Bootstrap; % This creates slot #3.s + SOM_CostFunction(3,SelfOMapN,SOM.Cost); + % SOMMem{slot}.theData = SOMMem{slot}.theDataO; + for iSOM = 1:nSOM; + % Only construct null distribution from voxels NOT associated with + % that exemplar. + % idxVoxel = ones(size(results.dataBySOMNull,1),1); + idxVoxel = ones(size(SOMMem{3}.dataBySOM,1),1); + % Find those voxels associated with this exemplar. + idxTmp = find(results.IDX == iSOM); + results.NAssoc(iSOM) = length(idxTmp); + if N > 0 + idxTmp = []; + end + idxVoxel(idxTmp) = 0; + idxTmp = find(idxVoxel==1); + if length(idxTmp) == 0 + fprintf('Fatal Error in Determining null distribution for %d exemplar, all voxels are associated!\n',iSOM); + else + tHist = hist(abs(SOMMem{3}.dataBySOM(idxTmp,iSOM)),(0:histUPPER/nPnts:histUPPER)); % Do just one tailed our cost function is symmetric. +% tHist = hist(abs(results.dataBySOMNull(idxTmp,iSOM)),(0:histUPPER/nPnts:histUPPER)); % Do just one tailed our cost function is symmetric. + if SOM.Cost ~= 0 & SOM.Cost ~= 3 + tHist = fliplr(tHist) + end + results.SOMHist(iSOM,:) = results.SOMHist(iSOM,:) + tHist; + end + end +end + +fprintf('Done with histogram building.\n'); + +% Now calculate the histograms for each SOM exemplar. +% Min and Max of the histograms are -1 and 1. + +results.xBins = [0:histUPPER/nPnts:histUPPER]; + +results.nEntries = N*size(SOMMem{3}.theData,1); + +results.SOMHistE = sqrt(results.SOMHist); + +% Place to put the fit of the gaussian and the theoritical curve. +results.SOMpdfParms = zeros(nSOM,2); +results.SOMpdfChi2 = zeros(nSOM,1); +results.SOMpdfNDFS = zeros(nSOM,1); +results.SOMpdfFit = zeros(nSOM,length(results.xBins)); + +% Make the theoritical pdfs have 10x the resolution of the histograms. +results.xBins10 = [0:histUPPER/(nPnts*10):histUPPER]; +results.SOMpdf = zeros(iSOM,length(results.xBins10)); +results.SOMcdf = zeros(iSOM,length(results.xBins10)); + +fprintf('Calculating pdf''s and cdf''s. Fitting curves\n'); + +for iSOM = 1:nSOM; + % Unit norm the histogram. + NormFactor = sum(results.SOMHist(iSOM,:)); + results.SOMHist(iSOM,:) = results.SOMHist(iSOM,:)/NormFactor; + results.SOMHistE(iSOM,:) = results.SOMHistE(iSOM,:)/NormFactor; + results.SOMCDFH(iSOM,:) = 1-cumsum(results.SOMHist(iSOM,:)); + results.SOMCDFE(iSOM,:) = sqrt(cumsum(results.SOMHistE(iSOM,:).^2)); + SOMGaussian.Y = results.SOMHist(iSOM,:); + SOMGaussian.Ye = results.SOMHistE(iSOM,:); + SOMGaussian.X = results.xBins; + [results.SOMpdfParms(iSOM,:) results.SOMpdfChi2(iSOM)] = ... + fminsearch(@SOM_FitGaussian,[max(SOMGaussian.Y) .08]); + results.SOMpdfFit(iSOM,:) = SOMGaussian.Yth; + results.SOMpdfNDFS(iSOM) = SOMGaussian.nDF-2; + results.SOMpdf(iSOM,:) = exp(-.5*((results.xBins10/ ... + results.SOMpdfParms(iSOM,2)).^2))*results.SOMpdfParms(iSOM,1); + % Unit norm the pdf + results.SOMpdf(iSOM,:) = results.SOMpdf(iSOM,:)/sum(results.SOMpdf(iSOM,:)); + results.SOMcdf(iSOM,:) = 1-cumsum(results.SOMpdf(iSOM,:)); +end + +% Find the index of a given weight for the voxels based on 1001 bins. +% Bounded by 1 and 1001. + +fprintf('Find P values for associated Exemplars.\n'); + +IWTS = max( [ ones(length(results.WTS),1) ... + min([(nPnts*10+1)*ones(length(results.WTS),1) floor((abs(results.WTS*nPnts*10)+.5)/histUPPER)],[],2)],[],2); +% Now the index for the whole array. + +fprintf('Calculating p-value indices\n'); + +% We use the cost-function that is stored in slot #1. + +IArray = reshape(max( [ ones(prod(size(SOMMem{slot}.dataBySOM)),1) ... + min([(nPnts*10+1)*ones(prod(size(SOMMem{slot}.dataBySOM)),1) ... + floor((abs(reshape(SOMMem{slot}.dataBySOM, ... + [prod(size(SOMMem{slot}.dataBySOM)) ... + 1])*nPnts*10)+.5)/histUPPER)],[],2)],[],2),[size(SOMMem{slot}.dataBySOM)]); + +IISOM = [0:nSOM-1]*(nPnts*10+1); + +% +% Now lookup the probability. +% + +% The table is P(iSOM,metric); + +fprintf('Looking up p-values.\n'); + +results.pVals = results.SOMcdf( ( results.IDX - 1 ) * size(results.SOMCDFH,2) + IWTS).*sign(results.WTS); + +SOMMem{slot}.pVals = 0*SOMMem{slot}.dataBySOM; + +crap = results.SOMcdf'; + +for iV = 1:size(SOMMem{slot}.dataBySOM,1) + SOMMem{slot}.pVals(iV,:) = crap(IArray(iV,:)+IISOM); +end + +clear crap + +% +% All done. +% + + + diff --git a/som/SOM_CalcRCutoff.m b/som/SOM_CalcRCutoff.m new file mode 100755 index 00000000..eab7010d --- /dev/null +++ b/som/SOM_CalcRCutoff.m @@ -0,0 +1,53 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2010 +% +% Calculate the cutoff value for r (lower and upper) based +% on the CDF of the r-values observed +% +% function results = SOM_CalcRCutoff(dataBySOM) +% +% results = [lower, upper] +% +% dataBySOM = can be any distribution of r-values. +% if 2-d, then dataBySOM = Space x R +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_CalcRCutoff(dataBySOM,pVal) + +if exist('pVal') == 0 + pVal = .05; +end + +if pVal > .5 | pVal < 0 + pVal = .05; +end + +dataBySOM = squeeze(dataBySOM); + +if size(dataBySOM,1) == 1 + dataBySOM = dataBySOM'; +end + +nEXP = size(dataBySOM,2); + +results = []; + +xBase = [-1:.01:1]; + +for iEXP = 1:nEXP + hR = hist(dataBySOM(:,iEXP),xBase); + cHR = cumsum(hR); + cHR = cHR/max(cHR); + i1 = find(cHR<=pVal); + i1 = max([1 max(i1)]); + i2 = find(cHR>=(1-pVal)); + i2 = min([max([min(i2) 1]) length(cHR)]); + results = [results ; xBase(i1) xBase(i2)]; +end + + + + \ No newline at end of file diff --git a/som/SOM_CalculateCorrelationImages.m b/som/SOM_CalculateCorrelationImages.m new file mode 100755 index 00000000..652ff95c --- /dev/null +++ b/som/SOM_CalculateCorrelationImages.m @@ -0,0 +1,105 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Calculate correlation images. +% +% INPUT +% +% D0 -- see SOM_PreProcessData +% parameters -- see SOM_PreProcessData and SOM_CalculateCorrelations +% +% OUTPUT +% +% results = -1 error +% array of output written. +% +% +% function results = SOM_CalculateCorrelationImages(D0,parameters) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_CalculateCorrelationImages(D0,parameters) + +global SOM + +% +% Initialize the output matrix. +% + +results = []; + +rMap = zeros(size(D0,1),1); +rMapVol = zeros(parameters.maskHdr.dim(1:3)); %this fails if user specified no masking %% This should now be fixed with change in SOM_PreProcessData - 2012-03-29 - RCWelsh + +for iROI = 1 : parameters.rois.nroisRequested + + if parameters.rois.ROIOK(iROI) + roiTC = mean(D0(parameters.rois.IDX{iROI},:),1); + + SOM_LOG('STATUS : Entering CorrCoeff Image Calculation'); + + rMap = 0*rMap; + pMap = 0*rMap; + + %for iV = 1:size(D0,1); + % [rMap(iV) pMap(iV)] = corr(D0(iV,:)',roiTC'); + %end + + % Much faster! + + [rMap pMap] = corr(D0',roiTC'); + + % Now turn into maps (r and p) and write out. + + rMapVol = 0*rMapVol; + pMapVol = ones(size(rMapVol)); + + rMapVol(parameters.maskInfo.iMask) = rMap; + pMapVol(parameters.maskInfo.iMask) = pMap; + + clear rMapHdr; + + rMapHdr.fname = fullfile(parameters.Output.directory,sprintf('rmap_%s_%04d.nii',parameters.Output.name,iROI)); + rMapHdr.mat = parameters.maskHdr.mat; + + % Make sure we write out float32..... + + rMapHdr.dim = parameters.maskHdr.dim(1:3); + rMapHdr.dt = [16 0]; + rMapHdr.descrip = sprintf('%s : %d',parameters.Output.description,iROI); + + % Now the probability map. + + pMapHdr = rMapHdr; + pMapHdr.fname = fullfile(parameters.Output.directory,sprintf('pmap_%s_%04d.nii',parameters.Output.name,iROI)); + + spm_write_vol(rMapHdr,rMapVol); + spm_write_vol(pMapHdr,pMapVol); + + % now transform the results to a z score. + + if exist(rMapHdr.fname) == 2 + clear Vo + Vi = spm_vol(rMapHdr.fname); + Vo.fname = fullfile(parameters.Output.directory,sprintf('zmap_%s_%04d.nii',parameters.Output.name,iROI)); + Vo.mat = Vi.mat; + Vo.dim = Vi.dim; + Vo.dt = [16 0]; + Vo.descrip = ['z-score for ' rMapHdr.descrip]; + spm_imcalc(Vi,Vo,'1/2*log((1+i1)./(1-i1))'); + else + SOM_LOG(sprintf('WARNING : I guess the corr didn''t work for ROI %d',iROI)); + end + end + results = strvcat(results,rMapHdr.fname); +end + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_CalculateCorrelationMaps.m b/som/SOM_CalculateCorrelationMaps.m new file mode 100755 index 00000000..2ad0074f --- /dev/null +++ b/som/SOM_CalculateCorrelationMaps.m @@ -0,0 +1,91 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Calculate correlation matrix. +% +% INPUT +% +% D0 -- see SOM_PreProcessData +% parameters -- see SOM_PreProcessData and SOM_CalculateCorrelations +% +% OUTPUT +% +% results = -1 error +% array of output written. +% +% +% function results = SOM_CalculateCorrelationMaps(D0,parameters) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_CalculateCorrelationMaps(D0,parameters) + +global SOM + +% +% Initialize the output matrix. +% + +SOM_LOG('STATUS : Entering CorrCoeff Map Calculation'); + +rMatrix = zeros(parameters.rois.nroisRequested,parameters.rois.nroisRequested); +pMatrix = ones(parameters.rois.nroisRequested,parameters.rois.nroisRequested); + +% Array of ROI time courses. + +roiTC = zeros(size(D0,2),parameters.rois.nroisRequested); + +% Create an array of our data that is Time x ROI# + +for iROI = 1 : parameters.rois.nroisRequested + roiTC(:,iROI) = mean(D0(parameters.rois.IDX{iROI},:),1); +end + +% Corr wants data as NxP1 and NxP2 and returns the correlation +% matrix of P1 x P2. + +[rMatrix pMatrix] = corr(roiTC,roiTC); + +% It is quite possible that some of the ROI's are messed up? That +% is they don't really exist, even though specified. We can easily +% known those out. + +for iROI = 1 : parameters.rois.nroisRequested + if parameters.rois.ROIOK(iROI) == 0 + rMatrix(iROI,:) = 0; + pMatrix(iROI,:) = 1; + rMatrix(:,iROI) = 0; + pMatrix(:,iROI) = 1; + if parameters.Output.saveroiTC ~= 0 + roiTC(:,iROI) = 0; % also zero out the time course if the rMat is censored + end + end +end + +% +% Now save the matrix and parameters. +% + +corrName = fullfile(parameters.Output.directory,[parameters.Output.name '_corr']); + +save(corrName,'rMatrix','pMatrix'); + +results = corrName; + +if parameters.Output.saveroiTC ~= 0 + roiTCName = fullfile(parameters.Output.directory,[parameters.Output.name '_roiTC']); + save(roiTCName,'roiTC'); + results=strvcat(results,roiTCName); +end + + + +return + +% +% All done. +% diff --git a/som/SOM_CalculateCorrelationMapsFaster.m b/som/SOM_CalculateCorrelationMapsFaster.m new file mode 100755 index 00000000..ffb21ef6 --- /dev/null +++ b/som/SOM_CalculateCorrelationMapsFaster.m @@ -0,0 +1,84 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Calculate correlation matrix. +% +% INPUT +% +% D0 -- see SOM_PreProcessData +% parameters -- see SOM_PreProcessData and SOM_CalculateCorrelations +% +% OUTPUT +% +% results = -1 error +% array of output written. +% +% +% function results = SOM_CalculateCorrelationMaps(D0,parameters) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_CalculateCorrelationMapsFaster(D0,parameters) + +global SOM + +% +% Initialize the output matrix. +% + +SOM_LOG('STATUS : Entering CorrCoeff Map Calculation'); + +rMatrix = zeros(parameters.rois.nroisRequested,parameters.rois.nroisRequested); +pMatrix = ones(parameters.rois.nroisRequested,parameters.rois.nroisRequested); + +% Array of ROI time courses. + +roiTC = zeros(size(D0,2),parameters.rois.nroisRequested); + +% Create an array of our data that is Time x ROI# + +for iROI = 1 : parameters.rois.nroisRequested + roiTC(:,iROI) = mean(D0(parameters.rois.IDX{iROI},:),1); +end + +% Corr wants data as NxP1 and NxP2 and returns the correlation +% matrix of P1 x P2. + +[rMatrix pMatrix] = corr(roiTC,roiTC); + +% It is quite possible that some of the ROI's are messed up? That +% is they don't really exist, even though specified. We can easily +% known those out. + +for iROI = 1 : parameters.rois.nroisRequested + if parameters.rois.ROIOK(iROI) == 0 + rMatrix(iROI,:) = 0; + pMatrix(iROI,:) = 1; + rMatrix(:,iROI) = 0; + pMatrix(:,iROI) = 1; + end +end + +% +% Now save the matrix and parameters. +% + +corrName = fullfile(parameters.Output.directory,[parameters.Output.name '_corr']); +paraName = fullfile(parameters.Output.directory,[parameters.Output.name '_parameters']); + +parameters.stopCPU = cputime; + +save(corrName,'rMatrix','pMatrix'); +save(paraName,'parameters','SOM'); + +results = strvcat(corrName,paraName); + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_CalculateCorrelationMapsSlow.m b/som/SOM_CalculateCorrelationMapsSlow.m new file mode 100755 index 00000000..46c88a14 --- /dev/null +++ b/som/SOM_CalculateCorrelationMapsSlow.m @@ -0,0 +1,72 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Calculate correlation matrix. +% +% INPUT +% +% D0 -- see SOM_PreProcessData +% parameters -- see SOM_PreProcessData and SOM_CalculateCorrelations +% +% OUTPUT +% +% results = -1 error +% array of output written. +% +% +% function results = SOM_CalculateCorrelationMaps(D0,parameters) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_CalculateCorrelationMapsSlow(D0,parameters) + +global SOM + +% +% Initialize the output matrix. +% + +SOM_LOG('STATUS : Entering CorrCoeff Map Calculation'); + +rMatrix = zeros(parameters.rois.nroisRequested,parameters.rois.nroisRequested); +pMatrix = ones(parameters.rois.nroisRequested,parameters.rois.nroisRequested); + +for iROI = 1 : parameters.rois.nroisRequested - 1 + if parameters.rois.ROIOK(iROI) + for jROI = iROI+1 : parameters.rois.nroisRequested + if parameters.rois.ROIOK(jROI) + tc1 = mean(D0(parameters.rois.IDX{iROI},:),1)'; + tc2 = mean(D0(parameters.rois.IDX{jROI},:),1)'; + [rMatrix(iROI,jROI) pMatrix(iROI,jROI)] = corr(tc1,tc2); + else + rMatrix(iROI,jROI) = -3; % Error + end + end + else + rMatrix(iROI,:) = -2; % Error + end +end + +% +% Now save the matrix and parameters. +% + +corrName = fullfile(parameters.Output.directory,[parameters.Output.name '_corr']); +paraName = fullfile(parameters.Output.directory,[parameters.Output.name '_parameters']); + +parameters.stopCPU = cputime; + +save(corrName,'rMatrix','pMatrix'); +save(paraName,'parameters','SOM'); + +results = strvcat(corrName,paraName); + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_CalculateCorrelations.m b/som/SOM_CalculateCorrelations.m new file mode 100755 index 00000000..33214a2c --- /dev/null +++ b/som/SOM_CalculateCorrelations.m @@ -0,0 +1,153 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% +% A process to calculate correlations in time-series data. +% +% It is assumed that the data being passed in has be pre-processed using +% SOM_PreProcessData. +% +% You have multiple options for specifying what to do with the correlations +% +% 1) If you pass a single ROI (either as a MNI coordinate or a ROI +% file) then it will produce a correlation map image and the +% Fisher-transform r-z Z image. +% +% 2) If you pass multiple ROI (either as MNI coordinates or ROI files) then +% it will produce either multiple output images in the results directory or +% a correlation map between the ROIs. +% +% INPUT +% +% D0 -- data table from SOM_PreProcessData +% +% parameters -- see SOM_PreProcesData +% +% .rois +% [ specify one or the other: "mni" or "files"] +% .mni +% .coordinates - table of n coordinates (x,y,z) +% .size - which size, 1, 7, 19, 27 voxels +% .XROI - optional array of user specficied size. +% .YROI see below on how to build it. +% .ZROI +% .files - table of ROI files +% +% .mask +% .File - full directory path and name to file. +% .MaskFLAG - 0 no mask, 1 mask +% +% +% .Output +% .correlation - 'maps' - save a single correlation matrix +% 'images' - save a single correlation image +% per ROI +% +% .directory - full directory path to output +% .name - name of output file (generic) +% +% +% OUTPUT +% +% results = -1 error +% array of output written. +% +% +% function results = SOM_CalculateCorrelations(D0,parameters) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +function results = SOM_CalculateCorrelations(D0,parameters) + +global SOM + +results = -1; + +parameters.startCPU.corr = cputime; + +% +% Check the roi parameters +% + +parameters.rois = SOM_CheckROIParameters(parameters); + +if parameters.rois.OK == -1 + SOM_LOG('FATAL ERROR : parameters.rois failed to meet criteria'); + return +end + +% +% Check the output information +% + +parameters.Output = SOM_CheckOutput(parameters); + +if parameters.Output.OK == -1 + SOM_LOG('FATAL ERROR : parameters.Output failed to meet criteria'); + return +end + +% +% Sanity check, if Output.type = 0, which is a correlation map, then you +% need at least 2 ROIs that survived any cleaning up. +% + +if parameters.Output.type == 0 & parameters.rois.nrois < 2 + SOM_LOG('FATAL ERROR : You specified output to be a correlation matrix, but insufficient number of ROIS'); + return +end + +% +% Okay - we can do the work now. +% + +% Take the ROI definitions and turn them into linear indices for +% calculations. + +parameters.rois = SOM_BuildROILinearIDX(parameters); + +% Now do the correlation work, either making maps or images. + +switch parameters.Output.type + + case 0 + % + % Correlation maps. + % + SOM_LOG('STATUS : Calling SOM_CalculateCorrelationMaps'); + results = SOM_CalculateCorrelationMaps(D0,parameters); + + case 1 + % + % Correlation images and Z-images + % + SOM_LOG('STATUS : Calling SOM_CalculateCorrelationImages'); + results = SOM_CalculateCorrelationImages(D0,parameters); + + otherwise + % + % Error case + % + SOM_LOG('FATAL ERROR : Somehow we are not doing correlation map or image output'); + return +end + +parameters.stopCPU.corr = cputime; + +% Now write out the parameters to a file. + +paraName = fullfile(parameters.Output.directory,[parameters.Output.name '_parameters']); +results = strvcat(results,paraName); + +save(paraName,'parameters','SOM'); + +return + +% +% All done. +% diff --git a/som/SOM_CalculateMap.m b/som/SOM_CalculateMap.m new file mode 100755 index 00000000..f875393f --- /dev/null +++ b/som/SOM_CalculateMap.m @@ -0,0 +1,490 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% function results = SOM_CalculateMap(theData,nIter,mapSwitch,nSOM,[SelfOMap],[slot]) +% +% theData = theData(nSpace,nTime); +% nSOM = number of basis functions in the SOM +% nIter = number of iterations. +% mapSwitch = vertex layout 'grid' = square layout +% 'sphere' = spherical layout +% slot = memory slot to use. 1 = time series data (default) +% 2 = super cluster use. +% 3 = boostrap probabilities. +% +% NOTE : The "slot" is used to help with memory management. +% Self-Organzing Map calculations are very memory +% intensive, so we are using global memory. However, +% when calculating the supercluster map it would be useful +% to not wipe out the data, so we have two slots +% available for memory. +% +% Please use them wisely. +% +% You will break the calculation if you place +% your time-series data in slot #2! +% +% Hopefully you can avoid "Out of memory" error by +% specifying slot 1 for the big time series matrix, and +% slot 2 for super cluster calculation. +% +% Between subjects you may need to execute the "pack" +% command. +% +% Worst case, trim you session, save appropriate +% things to disk and restart matlab. +% +% +% results = results structure, it will at least contain +% the resulting SOM. +% +% results = results.SOM (the map) +% results.IDX (for each space element which SOM it is related to +% best) +% results.WT (how much of the variance is explained by that) +% +% this is a work in progress! +% +% Currently the cost functionm is Cos(Theat) = U dot V +% where U and V have unit length. +% +% You can set the SOM parameters with +% +% global SOM +% +% SOM.sigma = Initial neighborhood size. +% SOM.sigmaTimeConstant = Rate the neighborhood shrinks. +% SOM.learningTimeConstant = Rate that learning changes. +% SOM.alpha = Initial learning rate +% SOM.Cost = Cost function. (0=U.V, +% 1=|U-V|, 2=|U-V|^2, 3=M.I.). +% NOTE U.V is fastest metric. +% SOM.nPnts = number of points in histogram range : 0 - 1[2]. +% NOTE used by SOM_CalcP. (2 is upper range for |U-V| +% cost, a guess. See "SOM_CalcP.m") +% SOM.saveHistory = History level to save. (0/1/2); +% 0 = none; +% 1 = some; +% 2 = full - memory intensive. +% SOM.WeightOption 0 = no weighting of components. +% 1 = weight by variance +% 2 = weight by st-dev +% SOM.silent 0 = verbose +% 1 = non-verbose +% +% +% SOM.maskThresh = 12.5% - by default. +% +% If you don't specify these parameters they will assume default +% values of: +% +% SOM.alpha = .1 (See "SOM_Alpha.m" on evolution) +% SOM.learningTimeConstant = 2; +% SOM.sigmaTimeConstant = .25; +% SOM.sigma = nSOM/2; guess? +% SOM.Cost = 0; (Opening angle cost function.) +% SON.nPnts = 1000; +% SOM.saveHistory = 1; - Save most of history, but +% not full weights. +% SOM.WeightOption = 0; +% +% Alpha (SOM_Alpha.m) changes according to: +% +% exp(-iter/(nIter * SOM.learningTimeConstant)); +% +% Neighborhood size changes according to: +% +% exp(-iter/(nIter * SOM.sigmaTimeConstant)); +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_CalculateMap(theData,nIter,map_switch,nSOM,SelfOMap,slot) + +global time; +global SOM +global SOMMem + +if ~isfield(SOM,'silent') + SOM.silent = false; +end + +% Determine whether a sphere or grid for the SOM examplar organization. + +if length(map_switch) == 6 & lower(map_switch) == 'sphere' + if ~isfield(SOM,'sphereIter') + SOM.sphereIter = 1000; + end + v = SOM_Sphere_Make(nSOM, SOM.sphereIter); + results.v2 = v(:,1:3); + results.vn = v; + v = v(:,1:3); + SOM.sphere_option = 1; +elseif length(map_switch) == 4 & lower(map_switch) == 'grid' + if sqrt(nSOM)~=floor(sqrt(nSOM)) + fprintf('Can only do square SOM!\n'); + fprintf('Forcing SOM to be square\n'); + nSOM = floor(sqrt(nSOM)+.5)^2; + fprintf('New size is : %d\n',nSOM); + end + v = zeros(nSOM,3); + [x,y] = meshgrid(1:sqrt(nSOM),1:sqrt(nSOM)); + v(:,1) = reshape(x,[nSOM,1]); + v(:,2) = reshape(y,[nSOM,1]); + SOM.sphere_option = 0; +else + fprintf('Not a valid map switch option, valid options are:\n'); + fprintf(' ''Sphere'' & ''Grid''\n'); +end + +% Keep track of the version. + +MajorVersion = 1; +MinorVersion = 101; + +SOM.Version = sprintf('%d.%03d',MajorVersion,MinorVersion); + +% Keep track of the time. + +results.StartTime=cputime; + +tic; + +% Which method to determine neighborhood (same code, just cleaned +% up?). + +if ~isfield(SOM,'OldMethod') + SOM.OldMethod = 0; +end + +% Check to see which memory slot + +if exist('slot') == 0 + slot = 1; + if ~SOM.silent + fprintf('Defaulting to use memory slot 1.\n'); + end +end + +if slot ~= 1 & slot ~= 2 + if ~SOM.silent + fprintf('Forcing to use slot #1.\n'); + end + slot = 1; +end + +if ~SOM.silent + fprintf('Using memory slot #%d\n',slot); +end + +SOM.slot = slot; + +% Check to see if the necessary fields exist. + +% The neighborhood size +if ~isfield(SOM,'sigma') + SOM.sigma = sqrt(nSOM)/2; + if ~SOM.silent + fprintf('SOM.sigma -> %f\n',SOM.sigma); + end +end + +% How quickly to modify the neighborhood size. +if ~isfield(SOM,'sigmaTimeConstant') + SOM.sigmaTimeConstant = 1/4; + if ~SOM.silent + fprintf('SOM.sigmaTimeConstant -> %f\n',SOM.sigmaTimeConstant); + end +end + +% How quickly to modify the map learning rate. +if ~isfield(SOM,'learningTimeConstant') + SOM.learningTimeConstant = 2; + if ~SOM.silent + fprintf('SOM.learningTimeConstant -> %f\n',SOM.learningTimeConstant); + end +end + +% The initial map learning rate. +if ~isfield(SOM,'alpha') + SOM.alpha = .1; + if ~SOM.silent + fprintf('SOM.alpha -> %f\n',SOM.alpha); + end +end + +% The cost function +if ~isfield(SOM,'Cost') + SOM.Cost = 0; + if ~SOM.silent + fprintf('SOM.Cost -> %f\n',SOM.Cost); + end +end + +% The saving history. +if ~isfield(SOM,'saveHistory') + SOM.saveHistory = 1; + if ~SOM.silent + fprintf('SOM.saveHistory -> %f\n',SOM.saveHistory); + end +end + +% The saving history. +if ~isfield(SOM,'WeightOption') + SOM.WeightOption = 0; + if ~SOM.silent + fprintf('SOM.WeightOption -> %f\n',SOM.WeightOption); + end +end + +% Make sure that the Weight Option is Valid. +SOM.WeightOption = SOM.WeightOption(1); + +if sum(ismember([0 1 2],SOM.WeightOption)) == 0 + SOM.WeightOption = 0; + if ~SOM.silent + fprintf('SOM.WeightOption -> %f\n',SOM.WeightOption); + end +end + +% Determine the size of the space we are dealing with. + +% Record the size of the SOM. + +SOM.nSOM = nSOM; +SOM.nIter = nIter; + +% This is if we have square grid, else the info is meaningless +% if we have spherical space. + +nGrid = sqrt(nSOM); +SOM.nGrid = nGrid; + +nSpace = size(theData,1); +nTime = size(theData,2); + +if ~SOM.silent + fprintf('Number of time points : %d\n',nTime); + fprintf('Number of space points : %d\n',nSpace); +end + +% Randomize our initial SOM and normalize the basis. +% +% Modified on 8/23/2005 to use normaly distributed +% elements of vector. This will make it uniformly +% distributed on a sphere in n-space. +% +% Found by doing google search and getting +% argument of Steve Rayhawk at Harvey Mudd. +% Seems to work by graphical validation - still need +% to find the paper for citation. See reference in +% Donald Knuth book: The Art of Computer Programming, vol2 +% Seminumerical Algorithms, Addison-Wesley, 1969. +% + +if exist('SelfOMap') == 1 & ~any([nTime nSOM]-size(SelfOMap)) + if ~SOM.silent + fprintf('Using passed initialized SelfOMap\n'); + end +else + if ~SOM.silent + fprintf('Randomizing SelfOMap\n'); + end + SelfOMap = randn(nTime,nSOM); + SelfOMap = SOM_UnitNormMatrix(SelfOMap,1); +end + +% Store a copy of the initial one. +iSelfOMap = SelfOMap; + +clear SOM_UnitNormMatrix; + +% Unit norm the data. We are not necessarily interested in effect +% size at the moment versus explanatory basis. We can +% get the "beta's" later! + +% This is not the best, as when SOM_SuperClusterEasy calls us, we globber +% what was in here. Not good! 2007-03-23. +% Come up with a mex solution? + +SOMMem{slot}.theData = SOM_UnitNormMatrix(theData,2); + +% Calculate the Variance for weights. +SOMMem{slot}.varData = var(SOMMem{slot}.theData,[],2); + +% Weights for biasing the SOM to voxels that have high variance but +% low population number. + +switch SOM.WeightOption + case 0 + SOMMem{slot}.Weights = ones(size(theData,1),1); + case 1 + SOMMem{slot}.Weights = var(SOMMem{slot}.theData,[],2); + case 2 + SOMMem{slot}.Weights = std(SOMMem{slot}.theData,[],2); + case default + SOMMem{slot}.Weights = ones(size(theData,1),1); +end +SOMMem{slot}.Weights = repmat(SOMMem{slot}.Weights,[1 size(SOMMem{slot}.theData,2)]); +clear theData + +clear SOM_UnitNormMatrix; + +% Ok, let's go iterate the map. At the moment +% we'll just stop the calculation after a sufficient number +% iterations. + +iter = 0; + +%--------------------------Ben added 6/12/07-------------------- + +if SOM.sphere_option %sphere distance map + lneighborDist = zeros(nSOM,nSOM); + for ii=1:nSOM + for jj=1:nSOM + lneighborDist(ii,jj) = abs(acos(dot(v(ii,:),v(jj,:)))*2); + end + end +else %grid distance mapping + neighborDist = SOM_NeighborDist(nGrid); + lneighborDist = reshape(neighborDist,[nGrid*nGrid nGrid*nGrid]); +end + +history = {}; + +pV_init = (SOMMem{slot}.theData.*SOMMem{slot}.Weights)'; +if ~SOM.silent + fprintf('iter,alpha,NeighSigma,cpu\n'); +end + +%mov = avifile('animated4.avi','compression','Cinepak') + +while iter < nIter + tic; + % Increment the iteration + iter = iter + 1; + % Learning function = g(time); + alpha = SOM_Alpha(iter,nIter); + % Determine the neighbor map. + % For each element of the SOM, there will be an associated + % neighborhood. The call should return the indices of these + % SOM vectors as well as the distance for use in the weight + % calculation. + NeighSigma = SOM.sigma*exp(-iter/nIter/SOM.sigmaTimeConstant); + if SOM.OldMethod == 0 + SOMNeighborMap = exp(-lneighborDist.^2/NeighSigma^2); + %keyboard; + else + SOMNeighborMap = SOM_NeighborMap([nGrid nGrid],NeighSigma); + end + + % Determine the SOM vectors the data are closest to and save the + % history. + [idx wts] = SOM_FindClosest(SelfOMap,slot); + if SOM.saveHistory > 0 + history{iter}.idx = idx; + history{iter}.wts = wts; + history{iter}.SelfOMap = SelfOMap; + history{iter}.sigma = NeighSigma; + history{iter}.alpha = alpha; + %history{iter}.neighMap = SOMNeighborMap(:,:,(nGrid-1)*floor(nGrid/2)); + if SOM.saveHistory > 1 + history{iter}.dataBySOM = SOMMem{slot}.dataBySOM; + end + history{iter}.cpuTime = cputime; + end + % + clear SOM_FindClosest; + % Now calculate the perturbation to the SOM. + dSelfOMap = 0*SelfOMap; + + nullSOM = 0*SelfOMap; + for iSOM = 1:nSOM + % Deterimine which data affect this SOM vector. + dI = find(idx==iSOM); + if length(dI) > 0 + % pertube all the same regardless of number of perturbers. + % + % Modified to allow heavier weighting based on variance of + % the time-series vector for a voxel. This is under the + % assumption that we want variance explained by the model. + % + % This also makes the assumption that signal is high + % variance? + % + % 2007.03.20 Robert C. Welsh + % + pV = sum(pV_init(:,dI),2); + %pV = sum(pV_init(:,dI),2)/sum(SOMMem{slot}.Weights(dI,1)); + %pV = SOM_UnitNormMatrix(pV,1); + %-------------------Ben changed 6/13/07----------------------- + if SOM.OldMethod == 0 + vlx = pV*reshape(SOMNeighborMap(:,iSOM),[1 nSOM]); + dSelfOMap = dSelfOMap + vlx; + else + dSelfOMap = dSelfOMap + SOM_ModBasis(pV,nullSOM, ... + SOMNeighborMap{iSOM},iter,nIter); + end + clear SOM_ModBasis; + end + end + % Update the SOM. + %dSelfOMap = dSelfOMap/(nSpace/nSOM); + SelfOMap = SelfOMap + alpha*dSelfOMap; + SelfOMap = SOM_UnitNormMatrix(SelfOMap,1); + if(any(any(isnan(SelfOMap)))) + % keyboard + end + clear SOM_UnitNormMatrix; + xx=toc; + if SOM.saveHistory > 0 + history{iter}.toc = xx; + end + if ~SOM.silent + fprintf('%03d %f %f %f\n',iter,alpha,NeighSigma,xx); + end +end +SelfOMap = SOM_UnitNormMatrix(SelfOMap,1); +%mov = close(mov); + +% All done, and now pack up the results to be sent back to the calling +% function. + +results.SelfOMap = SelfOMap; + +[results.IDX results.WTS results.DIDX results.DWTS] = SOM_FindClosest(SelfOMap,slot); + +clear SOM_FindClosest; + +% Final cost function evaluation. + +results.dataBySOM = SOMMem{slot}.dataBySOM; + +% Return a copy of the initial one used. + +results.iSelfOMap = iSelfOMap; +results.history = history; + +results.weights = []; +for iter = 1:nIter + results.weights = [results.weights sum(results.history{iter}.wts)]; +end + +% Record the configuation. + +results.SOM = SOM; +results.H = hist(results.IDX,[0:results.SOM.nSOM]); + +clear history; +clear SelfOMap; + +% Store the finish time. + +results.StopTime=cputime; + +return + +% +% All done. +% diff --git a/som/SOM_CalculateMapV2.m b/som/SOM_CalculateMapV2.m new file mode 100755 index 00000000..65288842 --- /dev/null +++ b/som/SOM_CalculateMapV2.m @@ -0,0 +1,428 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% function results = SOM_CalculateMap(theData,nSOM,nIter,[SelfOMap],[slot]) +% +% theData = theData(nSpace,nTime); +% nSOM = number of basis functions in the SOM +% nIter = number of iterations. +% slot = memory slot to use. 1 = time series data (default) +% 2 = super cluster use. +% 3 = boostrap probabilities. +% +% NOTE : The "slot" is used to help with memory management. +% Self-Organzing Map calculations are very memory +% intensive, so we are using global memory. However, +% when calculating the supercluster map it would be useful +% to not wipe out the data, so we have two slots +% available for memory. +% +% Please use them wisely. +% +% You will break the calculation if you place +% your time-series data in slot #2! +% +% Hopefully you can avoid "Out of memory" error by +% specifying slot 1 for the big time series matrix, and +% slot 2 for super cluster calculation. +% +% Between subjects you may need to execute the "pack" +% command. +% +% Worst case, trim you session, save appropriate +% things to disk and restart matlab. +% +% +% results = results structure, it will at least contain +% the resulting SOM. +% +% results = results.SOM (the map) +% results.IDX (for each space element which SOM it is related to +% best) +% results.WT (how much of the variance is explained by that) +% +% this is a work in progress! +% +% Currently the cost functionm is Cos(Theat) = U dot V +% where U and V have unit length. +% +% You can set the SOM parameters with +% +% global SOM +% +% SOM.sigma = Initial neighborhood size. +% SOM.sigmaTimeConstant = Rate the neighborhood shrinks. +% SOM.learningTimeConstant = Rate that learning changes. +% SOM.alpha = Initial learning rate +% SOM.Cost = Cost function. (0=U.V, +% 1=|U-V|, 2=|U-V|^2, 3=M.I.). +% NOTE U.V is fastest metric. +% SOM.nPnts = number of points in histogram range : 0 - 1[2]. +% NOTE used by SOM_CalcP. (2 is upper range for |U-V| +% cost, a guess. See "SOM_CalcP.m") +% SOM.saveHistory = History level to save. (0/1/2); +% 0 = none; +% 1 = some; +% 2 = full - memory intensive. +% SOM.WeightOption 0 = no weighting of components. +% 1 = weight by variance +% 2 = weight by st-dev +% +% If you don't specify these parameters they will assume default +% values of: +% +% SOM.alpha = .1 (See "SOM_Alpha.m" on evolution) +% SOM.learningTimeConstant = 2; +% SOM.sigmaTimeConstant = .25; +% SOM.sigma = nSOM/2; guess? +% SOM.Cost = 0; (Opening angle cost function.) +% SON.nPnts = 1000; +% SOM.saveHistory = 1; - Save most of history, but +% not full weights. +% SOM.WeightOption = 0; +% +% Alpha (SOM_Alpha.m) changes according to: +% +% exp(-iter/(nIter * SOM.learningTimeConstant)); +% +% Neighborhood size changes according to: +% +% exp(-iter/(nIter * SOM.sigmaTimeConstant)); +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_CalculateMap(theData,nIter,SelfOMap,slot) + +global time; +global SOM +global SOMMem +%--------------------------Ben added 6/28/07-------------------- +[b,v] = sphere_iter(2,1); +nSOM = length(v); + +% Keep track of the version. + +MajorVersion = 1; +MinorVersion = 001; + +SOM.Version = sprintf('%d.%03d',MajorVersion,MinorVersion); + +% Keep track of the time. + +results.StartTime=cputime; + +tic; + +% Which method to determine neighborhood (same code, just cleaned +% up?). + +if ~isfield(SOM,'OldMethod') + SOM.OldMethod = 0; +end + +% Check to see which memory slot + +if exist('slot') == 0 + slot = 1; + fprintf('Defaulting to use memory slot 1.\n'); +end + +if slot ~= 1 & slot ~= 2 + fprintf('Forcing to use slot #1.\n'); + slot = 1; +end + +fprintf('Using memory slot #%d\n',slot); + +SOM.slot = slot; + +% Check to see if the necessary fields exist. + +% The neighborhood size +if ~isfield(SOM,'sigma') + SOM.sigma = sqrt(nSOM)/2; + fprintf('SOM.sigma -> %f\n',SOM.sigma); +end + +% How quickly to modify the neighborhood size. +if ~isfield(SOM,'sigmaTimeConstant') + SOM.sigmaTimeConstant = 1/4; + fprintf('SOM.sigmaTimeConstant -> %f\n',SOM.sigmaTimeConstant); +end + +% How quickly to modify the map learning rate. +if ~isfield(SOM,'learningTimeConstant') + SOM.learningTimeConstant = 2; + fprintf('SOM.learningTimeConstant -> %f\n',SOM.learningTimeConstant); +end + +% The initial map learning rate. +if ~isfield(SOM,'alpha') + SOM.alpha = .1; + fprintf('SOM.alpha -> %f\n',SOM.alpha); +end + +% The cost function +if ~isfield(SOM,'Cost') + SOM.Cost = 0; + fprintf('SOM.Cost -> %f\n',SOM.Cost); +end + +% The saving history. +if ~isfield(SOM,'saveHistory') + SOM.saveHistory = 1; + fprintf('SOM.saveHistory -> %f\n',SOM.saveHistory); +end + +% The saving history. +if ~isfield(SOM,'WeightOption') + SOM.WeightOption = 0; + fprintf('SOM.WeightOption -> %f\n',SOM.WeightOption); +end + +% Make sure that the Weight Option is Valid. +SOM.WeightOption = SOM.WeightOption(1); + +if sum(ismember([0 1 2],SOM.WeightOption)) == 0 + SOM.WeightOption = 0; + fprintf('SOM.WeightOption -> %f\n',SOM.WeightOption); +end + +% Determine the size of the space we are dealing with. + +%if sqrt(nSOM)~=floor(sqrt(nSOM)) +% fprintf('Can only do square SOM!\n'); +% fprintf('Forcing SOM to be square\n'); +% nSOM = floor(sqrt(nSOM)+.5)^2; +% fprintf('New size is : %d\n',nSOM); +%end + +% Record the size of the SOM. +SOM.nSOM = nSOM; +SOM.nIter = nIter; + +nGrid = sqrt(nSOM); +SOM.nGrid = nGrid; + +nSpace = size(theData,1); +nTime = size(theData,2); + +fprintf('Number of time points : %d\n',nTime); +fprintf('Number of space points : %d\n',nSpace); + +% Randomize our initial SOM and normalize the basis. +% +% Modified on 8/23/2005 to use normaly distributed +% elements of vector. This will make it uniformly +% distributed on a sphere in n-space. +% +% Found by doing google search and getting +% argument of Steve Rayhawk at Harvey Mudd. +% Seems to work by graphical validation - still need +% to find the paper for citation. See reference in +% Donald Knuth book: The Art of Computer Programming, vol2 +% Seminumerical Algorithms, Addison-Wesley, 1969. +% + +if exist('SelfOMap') == 1 & ~any([nTime nSOM]-size(SelfOMap)) + fprintf('Using passed initialized SelfOMap\n'); +else + fprintf('Randomizing SelfOMap\n'); + SelfOMap = randn(nTime,nSOM); + SelfOMap = SOM_UnitNormMatrix(SelfOMap,1); +end + +% Store a copy of the initial one. +iSelfOMap = SelfOMap; + +clear SOM_UnitNormMatrix; + +% Unit norm the data. We are not necessarily interested in effect +% size at the moment versus explanatory basis. We can +% get the "beta's" later! + +% This is not the best, as when SOM_SuperClusterEasy calls us, we globber +% what was in here. Not good! 2007-03-23. +% Come up with a mex solution? + +SOMMem{slot}.theData = SOM_UnitNormMatrix(theData,2); + +% Calculate the Variance for weights. +SOMMem{slot}.varData = var(SOMMem{slot}.theData,[],2); + +% Weights for biasing the SOM to voxels that have high variance but +% low population number. + +switch SOM.WeightOption + case 0 + SOMMem{slot}.Weights = ones(size(theData,1),1); + case 1 + SOMMem{slot}.Weights = var(SOMMem{slot}.theData,[],2); + case 2 + SOMMem{slot}.Weights = std(SOMMem{slot}.theData,[],2); + case default + SOMMem{slot}.Weights = ones(size(theData,1),1); +end +SOMMem{slot}.Weights = repmat(SOMMem{slot}.Weights,[1 size(SOMMem{slot}.theData,2)]); +clear theData + +clear SOM_UnitNormMatrix; + +% Ok, let's go iterate the map. At the moment +% we'll just stop the calculation after a sufficient number +% iterations. + +iter = 0; + +%neighborDist = SOM_NeighborDist(nGrid); + +%lneighborDist = reshape(neighborDist,[nGrid nGrid nGrid*nGrid]); +%--------------------------Ben added 6/12/07-------------------- + + +lneighborDist = zeros(nSOM,nSOM); +for ii=1:nSOM + for jj=1:nSOM + lneighborDist(ii,jj) = abs(acos(dot(v(ii,:),v(jj,:)))*2); + end +end + +history = {}; + +fprintf('iter,alpha,NeighSigma,cpu\n'); + +%mov = avifile('animated4.avi','compression','Cinepak') + +while iter < nIter + tic; + % Increment the iteration + iter = iter + 1; + % Learning function = g(time); + alpha = SOM_Alpha(iter,nIter); + % Determine the neighbor map. + % For each element of the SOM, there will be an associated + % neighborhood. The call should return the indices of these + % SOM vectors as well as the distance for use in the weight + % calculation. + NeighSigma = SOM.sigma*exp(-iter/nIter/SOM.sigmaTimeConstant); + if SOM.OldMethod == 0 + SOMNeighborMap = exp(-lneighborDist.^2/NeighSigma^2); + %keyboard; + else + SOMNeighborMap = SOM_NeighborMap([nGrid nGrid],NeighSigma); + end + + % Determine the SOM vectors the data are closest to and save the + % history. + [idx wts] = SOM_FindClosest(SelfOMap,slot); + if SOM.saveHistory > 0 + history{iter}.idx = idx; + history{iter}.wts = wts; + history{iter}.SelfOMap = SelfOMap; + history{iter}.sigma = NeighSigma; + history{iter}.alpha = alpha; + %history{iter}.neighMap = SOMNeighborMap(:,:,(nGrid-1)*floor(nGrid/2)); + if SOM.saveHistory > 1 + history{iter}.dataBySOM = SOMMem{slot}.dataBySOM; + end + history{iter}.cpuTime = cputime; + end + % + clear SOM_FindClosest; + % Now calculate the perturbation to the SOM. + dSelfOMap = 0*SelfOMap; + nullSOM = 0*SelfOMap; + pV_init = SOMMem{slot}.theData.*SOMMem{slot}.Weights; + for iSOM = 1:nSOM + % Deterimine which data affect this SOM vector. + dI = find(idx==iSOM); + if length(dI) > 0 + % pertube all the same regardless of number of perturbers. + % + % Modified to allow heavier weighting based on variance of + % the time-series vector for a voxel. This is under the + % assumption that we want variance explained by the model. + % + % This also makes the assumption that signal is high + % variance? + % + % 2007.03.20 Robert C. Welsh + % + pV = sum(pV_init(dI,:),1)'/... + sum(SOMMem{slot}.Weights(dI,1)); +%tic +%sum(SOMMem{slot}.theData(dI,:)); +%toc +%tic +%SOMMem{slot}.Weights(dI); +%toc +%keyboard; + %-------------------Ben changed 6/13/07----------------------- + if SOM.OldMethod == 0 + vlx = pV*reshape(SOMNeighborMap(:,iSOM),[1 nSOM]); + %SOM_ModBasis(pV,nullSOM,SOMNeighborMap(:,iSOM)); + dSelfOMap = dSelfOMap + vlx; + else + dSelfOMap = dSelfOMap + SOM_ModBasis(pV,nullSOM, ... + SOMNeighborMap{iSOM},iter,nIter); + end + clear SOM_ModBasis; + end + end + % Update the SOM. + SelfOMap = SelfOMap + alpha*dSelfOMap; + SelfOMap = SOM_UnitNormMatrix(SelfOMap,1); + if(any(any(isnan(SelfOMap)))) + keyboard + end + clear SOM_UnitNormMatrix; + xx=toc; + if SOM.saveHistory > 0 + history{iter}.toc = xx; + end + fprintf('%03d %f %f %f\n',iter,alpha,NeighSigma,xx); +end +%mov = close(mov); + +% All done, and now pack up the results to be sent back to the calling +% function. + +results.SelfOMap = SelfOMap; + +[results.IDX results.WTS results.DIDX results.DWTS] = SOM_FindClosest(SelfOMap,slot); + +clear SOM_FindClosest; + +% Final cost function evaluation. + +results.dataBySOM = SOMMem{slot}.dataBySOM; + +% Return a copy of the initial one used. + +results.iSelfOMap = iSelfOMap; +results.history = history; + +results.weights = []; +for iter = 1:nIter + results.weights = [results.weights sum(results.history{iter}.wts)]; +end + +% Record the configuation. + +results.SOM = SOM; +results.H = hist(results.IDX,[0:results.SOM.nSOM]); + +clear history; +clear SelfOMap; + +% Store the finish time. + +results.StopTime=cputime; + +return + +% +% All done. +% diff --git a/som/SOM_CheckDataStructure.m b/som/SOM_CheckDataStructure.m new file mode 100644 index 00000000..16b69e63 --- /dev/null +++ b/som/SOM_CheckDataStructure.m @@ -0,0 +1,170 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Validate the data parameters +% pass for SOM_PreProcessData +% +% +% function data = SOM_CheckDataStructure(parameters) +% +% data. +% +% run{i}. +% +% P = full directory path to time-series data. +% +% MotionParameters = array of motion parameters +% +% MaskFLAG = 0 don't do any masking and grab all of the data +% = 1 mask using either what is in parameters.epi +% or by building a subject specific mask with +% SOM_CreateMask +% nTIME = number of time points to +% analyze. +% +% censorVector = vector of 0's and 1's on which +% specific TR's to include in the analysis, this is after +% the nTIME has trimmed the data. NOT REQUIRED. +% +% OK = -1 returned if bad +% 1 if things are okay. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +% Modified Nov 8, 2011 to have nTIME be part of data.run +% structure, previously it was part of the TIME.run structure. + +% 2012.01.17 - put in check for censor vector - RCWelsh + +function data = SOM_CheckDataStructure(parameters) + +data.OK = -1; + +if isfield(parameters,'data') == 0 + SOM_LOG('FATAL ERROR : Missing data structure from parameters'); + return +end + +data = parameters.data; +data.OK = -1; + +if isfield(data,'run') == 0 + SOM_LOG('FATAL ERROR : Missing run variable from parameters.data'); + return +end + +% Now loop on each run. + +for iRUN = 1:length(data.run) + if isfield(data.run(iRUN),'P') == 0 + SOM_LOG('FATAL ERROR : Missing P variable in parameters.data'); + return + end + + if ischar(data.run(iRUN).P) == 0 + SOM_LOG('FATAL ERROR : You need to specify a character array of files'); + return + end + + % Now check to see if the input files actually exist. + + if exist(data.run(iRUN).P(1,:),'file') == 0 + SOM_LOG(sprintf('FATAL ERROR : File %s does not exist',data.run(iRUN).P(1,:))); + return + end + + % Read in a single header for this run from the first file. + + data.run(iRUN).hdr = spm_vol(data.run(iRUN).P(1,:)); + + % How many points in this run? If the hdr is multi-dimensional + % than most likely a nifti file, and if P has more than one line + % than it is img/hdr. + + nTIME = max( [ length(data.run(iRUN).hdr) size(data.run(iRUN).P,1) ] ); + + if isfield(data.run(iRUN),'nTIME') == 0 + data.run(iRUN).nTIME = nTIME; + SOM_LOG(sprintf('STATUS : Determined that for run %d, there are %d time-points.',iRUN,nTIME)); + end + + if isfield(data.run(iRUN),'censorVector') + if length(data.run(iRUN).censorVector) ~= data.run(iRUN).nTIME + SOM_LOG(sprintf('FATAL : Specified censorVector of length %d for run %d does not match %d'),iRUN,data.run(iRUN).nTIME) + return + end + end + + % Check to see if the motion parameters are there + + if isfield(data.run(iRUN),'MotionParameters') == 1 + if size(data.run(iRUN).MotionParameters,1) ~= data.run(iRUN).nTIME + SOM_LOG('WARNING : Motion parameters length does not match expected number of time points'); + if length(data.run(iRUN).MotionParameters) > 0 & size(data.run(iRUN).MotionParameters,1) < data.run(iRUN).nTIME + SOM_LOG(sprintf('FATAL ERROR : Motion parameter array is too short for run %d',iRUN)); + return + end + end + else + data.run(iRUN).MotionParameters = []; + SOM_LOG(sprintf('STATUS : No motion parameters for run %d',iRUN)); + end + + % And now make sure all files for this run are consistent. + + for iFILE = 2:size(data.run(iRUN).P,1) + if exist(data.run(iRUN).P(iFILE,:),'file') == 0 + SOM_LOG(sprintf('FATAL ERROR : File %s does not exist'),data.run(iRUN).P(iFILE,:)); + return + else + thisHDR = spm_vol(data.run(iRUN).P(iFILE,:)); % fixed on 5/2/2012 + if SOM_SpaceVerify(data.run(iRUN).hdr,thisHDR) ~= 1 + SOM_LOG('FATAL ERROR : Error with consistent in-run image space definition.'); + return + end + end + end + +end + +% Now check that the headers of each run specifies the same space: + +for iRUN = 2:length(data.run) + if SOM_SpaceVerify(data.run(1).hdr,data.run(iRUN).hdr) ~= 1 + SOM_LOG('FATAL ERROR : Error with consistent cross-run image space definition.'); + return + end +end + +% Check to see what type of masking +% The masking flag will apply to all of the runs, else +% we'd end up with a different number of space points per run +% which does NOT make sense. + +if isfield(data,'MaskFLAG') == 0 + SOM_LOG('WARNING : Defaulting to using a mask on the data'); + data.MaskFLAG = 1; +end + +if isnumeric(data.MaskFLAG) == 0 + SOM_LOG('WARNING : data.MaskFLAG is not numeric, forcing it to 1'); + data.MaskFLAG = 1; +end + +if data.MaskFLAG ~= 0 + data.MaskFLAG = 1; +end + +% Everything is ok. + +data.OK = 1; + +return + + + + diff --git a/som/SOM_CheckMasks.m b/som/SOM_CheckMasks.m new file mode 100644 index 00000000..73a40c3f --- /dev/null +++ b/som/SOM_CheckMasks.m @@ -0,0 +1,92 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% A routine to loop on the different masks that are potentially +% used in the SOM code and to check them for correctness etc. +% +% function masks = SOM_CheckMasks(parameters) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function masks = SOM_CheckMasks(parameters) + +global SOM + +% Does 'masks' field exist? + +if isfield(parameters,'masks') == 0 + SOM_LOG('WARNING : You have not specified a ".masks" structure. Using defaults'); + masks.epi = []; + masks.grey = []; + masks.csf = []; + masks.white = []; +else + masks = parameters.masks; +end + +% Default is error state + +masks.OK = -1; + +% Files needed for masking? + +if isfield(masks,'grey') == 0 + masks.grey = []; +end + +masks.grey = SOM_ParseFileParam(masks.grey); + +if masks.grey.OK == -1 + SOM_LOG('FATAL ERROR : You specified an grey mask that doesn''t exist'); + return +end + +% White Matter ROI for regression? + +if isfield(masks,'white') == 0 + masks.white = []; +end + +masks.white = SOM_ParseFileParam(masks.white); + +if masks.white.OK == -1 + SOM_LOG('FATAL ERROR : You specified an white mask that doesn''t exist'); + return +end + +% CSF ROI? + +if isfield(masks,'csf') == 0 + masks.csf = []; +end + +masks.csf = SOM_ParseFileParam(masks.csf); + +if masks.csf.OK == -1 + SOM_LOG('FATAL ERROR : You specified an csf mask that doesn''t exist'); + return +end + +% If no common epi mask then we will use one create on the fly. + +if isfield(masks,'epi') == 0 + masks.epi = []; +else + masks.epi = SOM_ParseFileParam(masks.epi); + if masks.epi.OK == -1 + SOM_LOG('FATAL ERROR : You specified an epi mask that doesn''t exist'); + return + end +end + +% Okay, we made it this far so all good. + +masks.OK = 1; + +% +% all done. +% \ No newline at end of file diff --git a/som/SOM_CheckOutput.m b/som/SOM_CheckOutput.m new file mode 100755 index 00000000..1b6454ce --- /dev/null +++ b/som/SOM_CheckOutput.m @@ -0,0 +1,135 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% +% INPUT +% +% parameters -- see SOM_PreProcesData +% +% .rois +% .Output +% .correlation - 'maps' - save a single correlation matrix +% 'images' - save a single correlation image +% per ROI +% .directory - full directory path to output +% .name - name of output file (generic) +% .description - comment to add to image files. +% .saveroiTC - save roi time-courses when using 'maps'. +% +% +% OUTPUT +% +% Output +% +% .OK = 1 all okay, other wise not. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function Output = SOM_CheckOutput(parameters) + +% Default is error + +Output.OK = -1; + +% check for the "rois" field; + +if isfield(parameters,'Output') == 0 + SOM_LOG('FATAL ERROR : You need to specify ".Output" definitions'); + return +end + + +Output = parameters.Output; + +if ~isfield(Output,'saveroiTC') + Output.saveroiTC = 0; + SOM_LOG('STATUS : Using saveroiTC = 0'); +end + + +Output.OK = -1; + +% +% Make sure they have asked for "maps" or "images". +% + +if isfield(Output,'correlation') + if ischar(Output.correlation) + Output.correlation = lower(Output.correlation); + switch lower(Output.correlation(1)) + case 'm' + Output.type = 0; % Correlation map/mat file. This require more than 1 ROI! + case 'i' + Output.type = 1; % r-image and z-image. + otherwise + SOM_LOG('FATAL ERROR : Output type no specified'); + return + end + else + SOM_LOG('FATAL ERROR : Output type needs to be specified as string'); + return + end +else + SOM_LOG('FATAL ERROR : Correlation output type is missing'); + return +end + +% +% Now check for the output directory, it must exist +% + +if isfield(Output,'directory') + if exist(Output.directory,'dir') == 0 + SOM_LOG('WARNING : Output directory is not there, but will attempt to create'); + [mkS mkM mkID] = mkdir(Output.directory); + if mkS + SOM_LOG('WARNING : Success'); + else + SOM_LOG('FATAL WARNING : Can not create'); + return + end + end +else + SOM_LOG('FATAL WARNING : Can not create'); + return +end + +if isfield(Output,'name') == 0 + SOM_LOG('WARNING : Missing output name, will use generic'); + Outputname = 'rmap'; +else + Outputname = Output.name; +end + +% Now make sure there are no special characters in name, but allow '_'. + +CHAROK = isstrprop(Outputname, 'alphanum'); + +Output.name = []; + +for iCHAR = 1:length(CHAROK) + if CHAROK(iCHAR) | strcmp(Outputname(iCHAR),'_') + Output.name = [ Output.name Outputname(iCHAR) ] ; + else + SOM_LOG(sprintf('WARNING : Removing special character "%s" from Output.name',Outputname(iCHAR))); + end +end + +if isfield(Output,'description') == 0 + Output.description = 'Correlaton map'; + SOM_LOG('STATUS : Using generic file comment description.'); +end + +Output.OK = 1; + +return + +% +% All done +% + + diff --git a/som/SOM_CheckROIParameters.m b/som/SOM_CheckROIParameters.m new file mode 100644 index 00000000..9f929fa1 --- /dev/null +++ b/som/SOM_CheckROIParameters.m @@ -0,0 +1,326 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% +% See SOM_MakeSphereROI if you want sizes other than those below. +% +% Check the ROI parameters. +% +% INPUT +% +% parameters -- see SOM_PreProcesData +% +% .rois +% [ specify one or the other: "mni" or "files"] +% .mni +% .coordinates - table of n coordinates (x,y,z) +% .size - which size, 1, 7, 19, 27 voxels +% (default is 19) +% .XROI - optional array of user specficied size. +% .YROI see below on how to build it. +% .ZROI +% .files - table of ROI files +% +% .mask +% .File - full directory path and name to file. +% .MaskFLAG - 0 no mask, 1 mask +% +% OUTPUT +% +% rois +% +% .OK = 1 all okay, other wise not. +% +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +function rois = SOM_CheckROIParameters(parameters); + +%save SOM_CheckROIParameters_01 parameters + +% Some initialization of tables we need. + +% Single voxel + +XROI{1} = [ +0 ]; +YROI{1} = [ +0 ]; +ZROI{1} = [ +0 ]; + +% Intersectiong plus signs. + +XROI{2} = [ +0 +0 -1 +0 +1 +0 +0]; +YROI{2} = [ +0 -1 +0 +0 +0 +1 +0]; +ZROI{2} = [ -1 +0 +0 +0 +0 +0 +1]; + +% Neighbors in a cube, but don't include the corners. + +XROI{3} = [ +0 -1 +0 +1 +0 -1 +0 +1 -1 +0 +1 -1 +0 +1 +0 -1 +0 +1 +0]; +YROI{3} = [ -1 +0 +0 +0 +1 -1 -1 -1 +0 +0 +0 +1 +1 +1 -1 +0 +0 +0 +1]; +ZROI{3} = [ -1 -1 -1 -1 -1 +0 +0 +0 +0 +0 +0 +0 +0 +0 +1 +1 +1 +1 +1]; + +% Neighbors in a cube, but include the corners. + +XROI{4} = [-1 +0 +1 -1 +0 +1 -1 +0 +1 -1 +0 +1 -1 +0 +1 -1 +0 +1 -1 +0 +1 -1 +0 +1 -1 +0 +1 ]; +YROI{4} = [-1 -1 -1 +0 +0 +0 +1 +1 +1 -1 -1 -1 +0 +0 +0 +1 +1 +1 -1 -1 -1 +0 +0 +0 +1 +1 +1 ]; +ZROI{4} = [-1 -1 -1 -1 -1 -1 -1 -1 -1 +0 +0 +0 +0 +0 +0 +0 +0 +0 +1 +1 +1 +1 +1 +1 +1 +1 +1 ]; + +SIZENAMES = ['XROI';'YROI';'ZROI']; + +SIZEOPTIONS = [1 7 19 27]; + +% Default is error + +rois.OK = -1; + +% check for the "rois" field; + +if isfield(parameters,'rois') == 0 + SOM_LOG('FATAL ERROR : You need to specify ROI definitions'); + return +end + +rois = parameters.rois; + +rois.OK = -1; + +% Okay, let's determine if specifying mni coordinates or images. + +if isfield(rois,'mni') == 0 + if isfield(rois,'files') == 0 + SOM_LOG('FATAL ERROR : You need to specify at least ".mni" or ".files"'); + return + else + rois.type = 1; + end +else + rois.type = 0; +end + +% Now see if they specified a mask to constrain the ROI's +% The rois will ALSO be constrained by the mask as defined in +% parameters.masks.epi + +if isfield(rois,'mask') == 0 + rois.mask = []; +end + +rois.mask = SOM_ParseFileParam(rois.mask); + +if rois.mask.OK == -1 + SOM_LOG('FATAL ERROR : You specified a mask that does not exist'); + return +end + +% +% If the mask wasn't specified we will use the first image of the time series +% data as a mask as we need to make sure that the ROIS are in the brain. +% + +if isempty(rois.mask.File) + try + PMask = parameters.data.run(1).P(1,:); + SOM_LOG(sprintf('STATUS : Using %s as a masking image',parameters.data.run(1).P(1,:))); + catch + SOM_LOG('FATAL ERROR : No time series data specified yet.'); + return + end +else + PMask = rois.mask.File; +end + +% +% Now make sure that the mask specified matchs our data. +% + +PMaskHDR = spm_vol(PMask); + +if SOM_SpaceVerify(parameters.data.run(1).hdr,PMaskHDR) ~= 1 + SOM_LOG('FATAL ERROR : Error with consistent image space (ROI Mask) definition.'); + return +end + +% Now work through the configuration of the ROIs + +switch rois.type + + case 0 + + % + % this case is mni coordinates + % + + % Must have specified the array of MNI coordinates in the + % structure. + if isfield(rois.mni,'coordinates') == 0 + SOM_LOG('FATAL ERROR : You need to specificy MNI coordinates, ".coordinates" array is missing'); + return + end + + % And of course they need to be numeric. + if isnumeric(rois.mni.coordinates) == 0 + SOM_LOG('FATAL ERROR : You must specify numerical MNI coordinates'); + return + end + + % And the array needs to be N x 3 + if size(rois.mni.coordinates,2) ~= 3 + SOM_LOG('FATAL ERROR : Incorrect size of MNI ".coordinates" array'); + SOM_LOG('FATAL ERROR : You need to specify 3 coordinates (x,y,z) for each entry'); + return + end + + % Next, how big is each ROI, they will all be the same. + if isfield(rois.mni,'size') == 0 + SOM_LOG(sprintf('STATUS : size of ROIS not specified, defaulting to %d voxels',SOM.defaults.roi.mni.size)); + rois.mni.size = SOM.defaults.roi.mni.size; + end + + % You can either specify the size, or you can attach structure + % indicating the size of the ROI with the ".XROI", ".YROI" and + % ".ZROI" sizes. + if isnumeric(rois.mni.size) + whichSIZE = find(SIZEOPTIONS==rois.mni.size(1)); + if length(whichSIZE) < 1 + whichSIZE = 3; + SOM_LOG('WARNING : roi ".size" is not recognized, defaulting to 19 voxels'); + end + rois.mni.size = []; + rois.mni.size.XROI = XROI{whichSIZE}; + rois.mni.size.YROI = YROI{whichSIZE}; + rois.mni.size.ZROI = ZROI{whichSIZE}; + rois.mni.size.size = length(rois.mni.size.XROI); + else + for iXYZ = 1:3 + if isfield(rois.mni.size,SIZENAMES(iXYZ,:)) == 0 + SOM_LOG('FATAL ERROR : You need to specify either a size, or your own XROI, YROI, ZROI'); + end + end + rois.mni.size.size = length(rois.mni.size.XROI); + end + + % + % Now make sure that the sizes are all the same + % + + if any(size(rois.mni.size.XROI)-size(rois.mni.size.YROI)) + SOM_LOG('FATAL ERROR : size of XROI, YROI do not match'); + return + end + if any(size(rois.mni.size.XROI)-size(rois.mni.size.ZROI)) + SOM_LOG('FATAL ERROR : size of XROI, ZROI do not match'); + return + end + + % + % Now see if any of the coordinates are inside the masking image. + % + + % How many rois were passed? + rois.nroisRequested = size(rois.mni.coordinates,1); + + rois.mni.inIDX = SOM_roiPointsInMask(PMask,rois.mni.coordinates); + + rois.ROIOK = zeros(rois.nroisRequested,1); + + % For now each ROI will contain the same number of voxels, however, + % this will later get masked again so make sure that the ROI true + % extent doesn't go beyond the time series masking image. + rois.nvoxels = rois.mni.size.size*ones(rois.nroisRequested,1); + + % Mark which ROI's are good. + rois.ROIOK(rois.mni.inIDX) = 1; + + % How many valid ROIS really? + rois.nrois = length(rois.mni.inIDX); + + case 1 + + % + % this case is roi-files + % + + % How many rois were passed? + rois.nroisRequested = size(rois.files,1); + rois.nvoxels = zeros(rois.nroisRequested,1); + + % + % For now the voxel dimensionality of the ROI files must strictly + % adhere to the size of the input data. + % + + try + MVol = spm_read_vols(spm_vol(PMask)); + MVol = MVol > 0; + catch + SOM_LOG('FATAL ERROR : Can not figure out the mask'); + return + end + + %DATAHDR = spm_vol(parameters.data.run(1).P(1,:)); + + for iFILE = 1:rois.nroisRequested + thisFILE = strtrim(rois.files(iFILE,:)); + if exist(thisFILE,'file') == 0 + SOM_LOG(sprintf('FATAL ERROR : ROI file:%s does not exist.',thisFILE)); + return + end + % + % If the file doesn't adhere to SPM standards this will produce + % a fatal error. + % + try + rois.hdr(iFILE) = spm_vol(thisFILE); + + if SOM_SpaceVerify(parameters.data.run(1).hdr,rois.hdr(iFILE)) ~= 1 + SOM_LOG('FATAL ERROR : Error with consistent roi image space definition.'); + return + end + + % + % Count how many voxels in this ROI + % + SOM_LOG(sprintf('STATUS : read hdr for %s\n',thisFILE)); + thisVOL = spm_read_vols(rois.hdr(iFILE)); + rois.nvoxels(iFILE) = sum(sum(sum((thisVOL>0).*MVol))); + catch + SOM_LOG(sprintf('FATAL ERROR : spm could not read file %s',thisFILE)); + return + end + end + + rois.ROIOK = zeros(rois.nroisRequested,1); + rois.ROIOK(find(rois.nvoxels)) = 1; + + % How many valid ROIS really? + rois.nrois = length(find(rois.nvoxels)); +end + +% +% In either method, did any rois survive the masking? +% + +if rois.nrois < 1 + SOM_LOG('FATAL WARNING : No rois survived masking.'); + return +end + +SOM_LOG(sprintf('STATUS : %d rois survived masking',rois.nrois)); + +% +% If we got this far, then the ROI definitions are good. +% Other logic testing takes place in SOM_CalculateCorrelations. +% + +rois.OK = 1; + +return + +% +% All done +% + diff --git a/som/SOM_CheckRegressFLAGS.m b/som/SOM_CheckRegressFLAGS.m new file mode 100755 index 00000000..0a9349a3 --- /dev/null +++ b/som/SOM_CheckRegressFLAGS.m @@ -0,0 +1,148 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Validate the FLAG parameters +% pass for SOM_PreProcessData +% +% RegressFLAGS. +% +% prinComp = 0 use average if available +% # use [N] principle components specified +% +% motion = 0 no motion regression +% 1 motion regression (default if MotionParameters +% are present) +% THIS IS AN INTERNAL FLAG +% +% order = alphabetic order of signal processing. +% D = detrend +% G = global +% C = CSF +% W = white matter +% M = motion +% B = band pass +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +% Modified Nov 8, 2011 to have nTIME be part of data.run +% structure, previously it was part of the TIME.run structure. + +function RegressFLAGS = SOM_CheckRegressFLAGS(parameters) + +global SOM + +if isfield(parameters,'RegressFLAGS') == 0 + SOM_LOG('WARNING : No regression flags specified, going to build defaults'); + RegressFLAGS = []; + RegressFLAGS.prinComp = SOM.defaults.RegressFLAGS.prinComp; + RegressFLAGS.motion = 1; + for iRUN = 1:length(parameters.data.run) + % + % If motion parameters are missing for any run then we turn + % the regression off for all runs. + % + % + if isfield(parameters.data.run(iRUN),'MotionParameters') == 0 + parameters.data.run(iRUN).MotionParameters = []; + RegressFLAGS.motion = 0; + SOM_LOG(sprintf('WARNING : No motion parameters for run %d, setting RegressFLAGS.motion = 0',iRUN)); + end + % + % Fatal error if motion parameters are not sufficient. + % + if ~isnumeric(parameters.data.run(iRUN).MotionParameters) | ... + parameters.data.run(iRUN).nTIME > size(parameters.data.MotionParameters,1) + SOM_LOG(sprintf('FATAL ERROR : Problem with motion parameters for run %d',iRUN)); + return + else + RegressFLAGS.motion = 1; + end + end + % Default the order. + RegressFLAGS.order = SOM.defaults.RegressFLAGS.order; + RegressFLAGS.OK = 1; + return +end + +RegressFLAGS = parameters.RegressFLAGS; + +% Default to being okay. + +RegressFLAGS.OK = 1; + +if isfield(RegressFLAGS,'prinComp') == 0 + SOM_LOG('WARNING : prinComp FLAG no set, using default of 5'); + RegressFLAGS.prinComp = SOM.defaults.RegressFLAGS.prinComp; +end + +%if isfield(RegressFLAGS,'global') == 0 +% SOM_LOG('WARNING : global FLAG no set, using default of 0'); +% RegressFLAGS.global = SOM.defaults.RegressFLAGS.global; +%end + +%if isfield(RegressFLAGS,'csf') == 0 +% SOM_LOG('WARNING : csf FLAG no set, using default of parameters.masks.csf.MaskFLAG'); +% RegressFLAGS.csf = parameters.masks.csf.MaskFLAG; +%end + +%if isfield(RegressFLAGS,'white') == 0 +% SOM_LOG('WARNING : white FLAG no set, using default of parameters.masks.white.MaskFLAG'); +% RegressFLAGS.white = parameters.masks.white.MaskFLAG; +%end + +% Check the motion parameters for each run + +for iRUN = 1:length(parameters.data.run) + if isfield(parameters.data.run(iRUN),'MotionParameters') == 0 | length(parameters.data.run(iRUN).MotionParameters) < 1 + SOM_LOG('WARNING : Motion parameters not specified'); + MotionParameters = []; + RegressFLAGS.motion = 0; + else + MotionParameters = parameters.data.run(iRUN).MotionParameters; + + if ~isnumeric(MotionParameters) | ... + parameters.data.run(iRUN).nTIME > size(MotionParameters,1) + SOM_LOG('FATAL ERROR : motion parameters missing, or mismatched, not using.'); + return + else + if isfield(RegressFLAGS,'motion') == 0 + SOM_LOG('WARNING : motion flag not there, but motion parameters present, setting flag to 1.'); + RegressFLAGS.motion = 1; + end + end + end +end + +% The regression order. + +if isfield(RegressFLAGS,'order') == 0 + SOM_LOG(sprintf('WARNING : Assuming regression order of : %s',SOM.defaults.RegressFLAGS.order)); + RegressFLAGS.order = SOM.defaults.RegressFLAGS.order; +else + if ischar(RegressFLAGS.order) == 0 + SOM_LOG('FATAL : Regression order must be a character array'); + RegressFLAGS.OK = -1; + return + end + nOrder = ''; + for iOrder = 1:length(RegressFLAGS.order) + if length(findstr(upper(RegressFLAGS.order(iOrder)),'DGCWMB')) > 0 + nOrder = [nOrder upper(RegressFLAGS.order(iOrder))]; + else + SOM_LOG(sprintf('FATAL : I don''t recognize the regression order symbol : %s',RegressFLAGS.order(iOrder))); + RegressFLAGS.OK = -1; + return + end + end + RegressFLAGS.order = nOrder; +end + +return + +% +% All done. +% diff --git a/som/SOM_CheckTimeParams.m b/som/SOM_CheckTimeParams.m new file mode 100755 index 00000000..53cb9760 --- /dev/null +++ b/som/SOM_CheckTimeParams.m @@ -0,0 +1,139 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Validate the time parameters +% pass for SOM_PreProcessData +% +% TIME = SOM_CheckTimeParams(parameters) +% +% TIME.run(iRUN). +% TR = repetition time +% BandFLAG = 0 no band pass filter +% 1 apply bandpass filter +% TrendFLAG < 0 no linear detrending +% # use [N]-order polynomial to detrend. +% LowF = low frequency band cut +% HiF = high frequency band cut +% gentle = 0, no rolling +% 1, rolling +% padding = # time points to pad on left/right +% whichFilter = 0, use the MATLAB filter +% #, use SOM_Filter_FFT +% +% fraction = fraction of variance for principle components +% analysis. Default 1. +% +% Returne TIME.OK = 1 if all okay, else -1 if bad. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +% Modified Nov 8, 2011 to have nTIME be part of data.run +% structure, previously it was part of the TIME.run structure. + +function TIME = SOM_CheckTimeParams(parameters) + +global SOM + +% Figure out if parameters are okay. + +TIME = []; + +if isfield(parameters,'TIME') == 0 + SOM_LOG('WARNING : Missing TIME parameters'); +else + TIME = parameters.TIME; +end + +% Default to be all OK. + +TIME.OK = 1; + +if isfield(TIME,'run') == 0 + SOM_LOG('WARNING : Missing run specific timing information'); + SOM_LOG('WARNING : I will create using defaults for each run.'); + for iRUN = 1:length(parameters.data.run) + TIME.run(iRUN) = SOM.defaults.TIME; + end + return +end + +% If the number of runs specified by TIME.run == 1, but we have +% more runs than that specified by data.run, then we will replicate +% for each run. + +if length(TIME.run) == 1 & length(parameters.data.run) > 1 + SOM_LOG('STATUS : Replicating timing parameters for all runs.\n'); + % Make the passed the new defaults. + SOM.defaults.TIME = TIME.run(1); + TIME = rmfield(TIME,'run'); + %TIME.run = []; + for iRUN = 1:length(parameters.data.run) + TIME.run(iRUN) = SOM.defaults.TIME; + end + return +end + +% If they have sufficient then we just check each parameter to make +% sure it is present. + +for iRUN = 1:length(TIME.run) + if isfield(TIME.run(iRUN),'TR') == 0 + SOM_LOG(sprintf('WARNING : Missing TIME.run(%d).TR, using default of TR=2',iRUN)); + TIME.run(iRUN).TR = SOM.defaults.TIME.TR; + end + + if isfield(TIME.run(iRUN),'BandFLAG') == 0 + SOM_LOG(sprintf('WARNING : Missing TIME.run(%d).BandFLAG, setting to 1',iRUN)); + TIME.run(iRUN).BandFLAG = SOM.defaults.TIME.BandFLAG; + end + + if isfield(TIME.run(iRUN),'TrendFLAG') == 0 + SOM_LOG(sprintf('WARNING : Missing TIME.run(%d).TrendFLAG, setting to 1',iRUN)); + TIME.run(iRUN).TrendFLAG = SOM.defaults.TIME.TrendFLAG; + end + + if isfield(TIME.run(iRUN),'LowF') == 0 + SOM_LOG(sprintf('WARNING : Missing TIME.run(%d).LowF, setting to 0.01',iRUN)); + TIME.run(iRUN).LowF = SOM.defaults.TIME.LowF; + end + + if isfield(TIME.run(iRUN),'HiF') == 0 + SOM_LOG(sprintf('WARNING : Missing TIME.run(%d).HiF, setting to 0.10',iRUN)); + TIME.run(iRUN).HiF = SOM.defaults.TIME.HiF; + end + + if isfield(TIME.run(iRUN),'padding') == 0 + SOM_LOG(sprintf('WARNING : Missing TIME.run(%d).padding, setting to 10',iRUN)); + TIME.run(iRUN).padding = SOM.defaults.TIME.padding; + end + + if isfield(TIME.run(iRUN),'gentle') == 0 + SOM_LOG(sprintf('WARNING : Missing TIME.run(%d).gentle, setting to 1',iRUN)); + TIME.run(iRUN).gentle = SOM.defaults.TIME.gentle; + end + + if isfield(TIME.run(iRUN),'whichFilter') == 0 + SOM_LOG(sprintf('WARNING : Missing TIME.run(%d).whichFilter, setting to 1',iRUN)); + TIME.run(iRUN).whichFilter = SOM.defaults.TIME.whichFilter; + end + + if isfield(TIME.run(iRUN),'fraction') == 0 + SOM_LOG(sprintf('WARNING : Missing TIME.run(%d).fraction, setting to 1',iRUN)); + TIME.run(iRUN).fraction = SOM.defaults.TIME.fraction; + end + + if TIME.run(iRUN).LowF >= TIME.run(iRUN).HiF + SOM_LOG(sprintf('FATAL ERROR : low frequency equal or exceed high frequency cut off. run:%d',iRUN)); + TIME.OK = -1; + end + +end + +% All done. + +return diff --git a/som/SOM_CostFunction.c b/som/SOM_CostFunction.c new file mode 100755 index 00000000..32d8dd5b --- /dev/null +++ b/som/SOM_CostFunction.c @@ -0,0 +1,572 @@ +/*----------------- +% +% Copyright Robert C. Welsh, Ann Arbor, MI, 2006 +% +% A routine to calculate the cost metrix +% between U and V where U are data (nVoxels,nTime) and +% V is SOM Exemplars (nTime,nVoxels); +% +% results = SOM_CostFunction(theData,SelfOMap,[whichCOST]); +% +% Input: +% +% theData = theData(nVoxels,nTime) +% SelfOMap = SelfOMap(nTime,nSOM); +% +% Optional: +% +% whichCOST = 0 - U.V (opening angle) (default) +% 1 - |U-V| (normalized euclidean distance). +% 2 - |U-V|^2 +% 3 - Mutual Information +% Output: +% +% results = results(nVoxels,nSOM) -> Cost Function. +% +% To compile do: +% +% mex [-DSOMDEBUG] [-DSOMDEBUG2] SOM_CostFunction.c +% +% where -DSOMDEBUG[2] is a debug flag to the compiler. +% +% +% -Robert Welsh, 2006-12-12. +% +%------------------*/ + +#include +#include "mex.h" + +/*// Define a ^2 function to use in Euclidean Distance to avoid using math routine "pow", it's too slow!*/ +#define SQR(a) (a*a) + +#define EPS 1e-10 + +/* + Results area - make it global so we can access it for multiple calls. +*/ + +static mxArray *resultsMX=NULL; + +static mxArray *theDataTMX=NULL; + +static double *timeSeries=NULL; + +/* + Exit routine - need to use "clear SOM_CostFunction" to free up the memory. +*/ + +static void SOM_ExitCost(void) +{ + + mexPrintf("SOM_ExitCost has been called.\n"); + if (resultsMX != NULL) + { + mexPrintf("Destroying persistent\n"); + mxDestroyArray(resultsMX); + resultsMX = NULL; + } + if (theDataTMX != NULL) + { + mexPrintf("Destroying persistent 'theDataTMX'\n"); + mxDestroyArray(theDataTMX); + theDataTMX = NULL; + } + if (timeSeries != NULL) + { + mexPrintf("Removing 'timeSeries'\n"); + mxFree(timeSeries); + timeSeries = NULL; + } +} + +/* + + Routine to calculate the Mutual Inforation Cost Function. + Based loosely on some code from Luis Hernandez. + + *** WARNING *** Mutual Information calculation is really slow!!! + +*/ + +float SOM_MutualInformation(double data[], double som[], int npnts) +{ + + int iPnt; + + double minx, maxx, deltax, miny, maxy, deltay; + + int nx, nxx; + + int iX, iY; + + double MI; + + /* Dynamically create the histograms: joint, x, and y */ + + double *JH, *HX, *HY; + + double histIncr; + + /* Make sure we have enough data points to histogram */ + + if (npnts < 2) + { + mexPrintf("The number of times points < 2, silly.\n"); + mexErrMsgTxt("Aborting."); + }; + + /* Ok, determine limits of histograms for calculating entropy */ + + minx = 1e9; + miny = 1e9; + maxx = -1e9; + maxy = -1e9; + + /* Find the limits of the histograms. */ + + for (iPnt = 0;iPnt < npnts;iPnt++) + { + minx = ( (minxdata[iPnt]) ? maxx : data[iPnt] ); + maxy = ( (maxx>som[iPnt]) ? maxy : som[iPnt] ); + } + + /* Determine number of elements in each histogram direction */ + + nx = (int) ( pow( (float)npnts , (float)(1./3.) ) + .5); + + deltax = (maxx-minx)/(nx-1); + deltay = (maxy-miny)/(nx-1); + + /* now add a bin for underflows and overflows */ + + nxx = nx + 2; + + if ( nx < 2 ) + { + mexPrintf("\nSOM_CostFunction.c:\n Histogram size needs to be at least 2x2\n\n"); + mexErrMsgTxt("Aborting."); + } + + /* Request memory from matlab heap for histograms */ + + JH = mxCalloc((nxx*nxx),sizeof(double)); + HX = mxCalloc(nxx,sizeof(double)); + HY = mxCalloc(nxx,sizeof(double)); + + /* Accumulate the histograms, use the following + for the histogram increment, that way it will be + unit normalized already */ + + histIncr = (double) (1.0/( (double) npnts)); + + for (iPnt = 0; iPnt < npnts; iPnt ++) + { + /* Calculate index and min and max it.*/ + + iX = (int) ((data[iPnt]-minx)/deltax + 1.5); + iY = (int) ((som[iPnt]-miny)/deltay + 1.5); + +#if SOMDEBUG2 + mexPrintf("%d %d\n",iX,iY); +#endif + + iX = (iX < 0) ? 0 : iX; + iY = (iY < 0) ? 0 : iY; + + iX = (iX > nx+1) ? nx+1 : iX; + iY = (iY > nx+1) ? nx+1 : iY; + + HX[iX] += histIncr; + HY[iY] += histIncr; + JH[iX*nxx+iY] += histIncr; + } + +#if SOMDEBUG2 + for (iX = 0; iX <= nx+1; iX ++) + { + for (iY = 0; iY <= nx+1 ; iY++) + mexPrintf("%f ",JH[iX*nxx+iY]); + mexPrintf("\n"); + } + mexPrintf("\n"); +#endif + + /* Now calculate the entropy, include overflow and underflow bins */ + + MI = 0; + + for (iX = 0; iX < nxx; iX ++) + for (iY = 0; iY < nxx ; iY++) + if ( HX[iX] > 0 && HY[iY] > 0 ) + { +#if SOMDEBUG2 + mexPrintf("%f %f %f %d %d\n",JH[iX*nxx+iY],HX[iX],HY[iY],iX,iY); +#endif + MI += JH[iX*nxx+iY]*log( ( (JH[iX*nxx+iY]+EPS)/HX[iX]/HY[iY] ) ); + } + +#if SOMDEBUG + mexPrintf("min/max : %f %f %f\n",minx,maxx,deltax); + mexPrintf("min/max : %f %f %f\n",miny,maxy,deltay); + mexPrintf("\n"); +#endif + + /* Free up the memory */ + + mxFree(JH); + mxFree(HX); + mxFree(HY); + +#if SOMDEBUG + mexPrintf("MI : %f\n",MI); +#endif + + return MI; + +} + +void SOM_CostFunctionUsage() +{ + mexPrintf("\nUsage : results = SOM_CostFunction(theData,SelfOMap,whichCOST)\n\n"); + mexPrintf(" theData -> theData(nVoxels,nTime)\n"); + mexPrintf(" SelfOMap -> theData(nTime,nSOM)\n"); + mexPrintf(" whichCOST -> 0=U.V, 1=|U-V|, 2=|U-V|^2, 3=Mutual Information\n\n"); +} + +/* + This is the main function that is called by MATLAB + + The routine will return a matrix of the cost-function evaluation. + + All of the cost functions are contained in here, except mutual information + which is lengthy code-wise and hence is above. + +*/ + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + mxArray *theDataMX; + mxArray *SelfOMapMX; + + /*mxArray *theDataTMX; /* Needed for mutual information calculation.*/ + + mxArray *tmpMX[2]; + + mxArray *whichCOSTMX; + + double *whichCOST; + + double *results; + + double *theData; + double *SelfOMap; + + double *theDataT; /* Needed for mutual information */ + + int nTime; + int nTimeSOM; + + int nSOM; + int nVoxels; + + int iSOM; + int iTime; + int iVoxel; + + int i1; + + int iPnt; + int COSTFLAG; + + double tmp1; + double tmp2; + + int idx1; + int idx2; + + /*// Now the function.*/ + + /* Register the exit function */ + + mexAtExit(SOM_ExitCost); + + /*// Did they pass in enough arguements?*/ + + COSTFLAG = 0; + + if (nrhs < 2 | nrhs > 3) + { + SOM_CostFunctionUsage(); + mexErrMsgTxt("Error, wrong number of input parameters."); + } + + /*// get the cost function option. 0 = U.V, 1 = |U-V|*/ + + if (nrhs == 3) + { + whichCOSTMX = prhs[2]; + if (mxGetM(whichCOSTMX) != 1 | mxGetN(whichCOSTMX) != 1) + { + SOM_CostFunctionUsage(); + mexErrMsgTxt("Error, 'whichCOST' must be a scaler."); + } + whichCOST = mxGetPr(whichCOSTMX); + switch ((int) whichCOST[0]) + { + case 3: + { +#if SOMDEBUG + mexPrintf("Using mutual information cost function.\n"); +#endif + COSTFLAG=3; + } + break; + case 2: + { +#if SOMDEBUG + mexPrintf("Using |U-V|^2 cost function.\n"); +#endif + COSTFLAG=2; + } + break; + case 1: + { +#if SOMDEBUG + mexPrintf("Using |U-V| cost function.\n"); +#endif + COSTFLAG=1; + } + break; + default: + { +#if SOMDEBUG + mexPrintf("Using U.V cost function.\n"); +#endif + COSTFLAG=0; + } + break; + } + } + + /*// Get the pointers to the data and self-organizing map.*/ + + theDataMX = prhs[0]; + SelfOMapMX = prhs[1]; + + /*// Now get the dimensions of each.*/ + + nVoxels = mxGetM(theDataMX); + nTime = mxGetN(theDataMX); + + nTimeSOM = mxGetM(SelfOMapMX); + nSOM = mxGetN(SelfOMapMX); + + theData = mxGetPr(theDataMX); + + SelfOMap = mxGetPr(SelfOMapMX); + + /*// Make sure the dimensions are good.*/ + if (nTime != nTimeSOM) + { + SOM_CostFunctionUsage(); + mexPrintf("Error, time points in 'SelfOMap' and time points of 'theData' don't match!\n"); + mexErrMsgTxt("Aborting."); + }; + +#if SOMDEBUG + mexPrintf("theData(%d,%d), SelfOMap(%d,%d)\n",nVoxels,nTime,nTimeSOM,nSOM); + mexPrintf("number of returns : %d\n",nlhs); +#endif + + /*// Results returned in a new matrix.*/ + + if (resultsMX == NULL) + { + mexPrintf("resultsMX is unknown\n"); + resultsMX = mxCreateDoubleMatrix(nVoxels,nSOM,mxREAL); + mexMakeArrayPersistent(resultsMX); + mexPrintf("Created persistent resultsMX array.\n"); + timeSeries = mxMalloc(sizeof(double)*nTime); + mexMakeMemoryPersistent(timeSeries); + mexPrintf("Created 'timeSeries' array\n"); + } + else + { + /* Check it's size */ + if (mxGetM(resultsMX) != nVoxels || mxGetN(resultsMX) != nSOM) + { + mexPrintf("resultsMX is known, %d\n",resultsMX); + mexPrintf("but, wrong size and must destroy persistent array and recreate.\n"); + mxDestroyArray(resultsMX); + resultsMX = mxCreateDoubleMatrix(nVoxels,nSOM,mxREAL); + mexMakeArrayPersistent(resultsMX); + mxFree(timeSeries); + timeSeries = mxMalloc(sizeof(double)*nTime); + mexMakeMemoryPersistent(timeSeries); + mexPrintf("Recreated 'timeSeries' array\n"); + } + } + + plhs[0] = resultsMX; + +#if SOMDEBUG + mexPrintf("Created output array of size %d x %d\n",mxGetM(plhs[0]),mxGetN(plhs[0])); +#endif + + /*// get the pointer to the data area of the output array.*/ + + results = mxGetPr(plhs[0]); + + /*// Now run the cost function.*/ + +#if SOMDEBUG + mexPrintf("COSTFLAG:%d\n",COSTFLAG); +#endif + switch (COSTFLAG) + { + case 0: + /* + simple U.V multiplication + */ +#if SOMDEBUG + mexPrintf("U.V\n"); +#endif + tmpMX[0] = theDataMX; + tmpMX[1] = SelfOMapMX; + /* + Just let matlab do the calculation + In a way this is silly but at least + then the interface for SOM_CostFunction + is the same. When we do the multiplication + ourselves it is too slow. + */ + mexCallMATLAB(1,&plhs[0],2,tmpMX,"*"); + break; + case 1: + /* + Euclidean distance. + */ +#if SOMDEBUG + mexPrintf("|U-V|\n"); +#endif + for (iVoxel = 0; iVoxel < nVoxels; iVoxel++) + { + /* Pull the time series for this voxel since we keep using it */ + for ( iTime = 0; iTime < nTime; iTime++) + timeSeries[iTime] = theData[iVoxel+iTime*nVoxels]; + /* */ + for (iSOM = 0; iSOM < nSOM; iSOM++) + { + idx1 = iSOM*nTime; + idx2 = nVoxels*iSOM; + tmp1 = 0; + for ( iTime = 0; iTime < nTime; iTime++) + { +#if SOMDEBUG2 + mexPrintf("|%f - %f|^2\n",theData[iVoxel+iTime*nVoxels],SelfOMap[idx1+iTime]); +#endif + tmp2 = timeSeries[iTime]-SelfOMap[idx1+iTime]; + tmp1 += tmp2*tmp2; + } + /* Now store the answer*/ + results[idx2+iVoxel] = sqrt(tmp1); +#if SOMDEBUG2 + mexPrintf("\n"); +#endif + } + } + break; + case 2: + /* + Euclidean distance squared. + */ +#if SOMDEBUG + mexPrintf("|U-V|^2\n"); +#endif + for (iVoxel = 0; iVoxel < nVoxels; iVoxel++) + { + /* Pull the time series for this voxel since we keep using it */ + for ( iTime = 0; iTime < nTime; iTime++) + timeSeries[iTime] = theData[iVoxel+iTime*nVoxels]; + /* */ + for (iSOM = 0; iSOM < nSOM; iSOM++) + { + idx1 = iSOM*nTime; + idx2 = nVoxels*iSOM; + tmp1 = 0; + for ( iTime = 0; iTime < nTime; iTime++) + { +#if SOMDEBUG2 + mexPrintf("|%f - %f|^2\n",theData[iVoxel+iTime*nVoxels],SelfOMap[idx1+iTime]); +#endif + tmp2 = timeSeries[iTime]-SelfOMap[idx1+iTime]; + tmp1 += tmp2*tmp2; + } + /* Now store the answer*/ + results[idx2+iVoxel] = tmp1; + +#if SOMDEBUG2 + mexPrintf("\n"); +#endif + } + } + break; + case 3: + /* + Mutual Information. + */ +#if SOMDEBUG + mexPrintf("M.I.\n"); +#endif + /* Transpose the data matrix to be able to index it via c like indexing.*/ + + /* Does the transpose area already exist, if so the right size. If so just use + the allocated space. This helps memory management.*/ + + if (theDataTMX == NULL) + { + mexPrintf("theDataTMX is unknown\n"); + theDataTMX = mxCreateDoubleMatrix(nVoxels,nSOM,mxREAL); + mexMakeArrayPersistent(theDataTMX); + mexPrintf("Created persistent theDataTMX array.\n"); + } + else + { + if ( mxGetM(theDataTMX) != mxGetN(theDataMX) || mxGetN(theDataTMX) != mxGetM(theDataMX) ) + { + mexPrintf("theDataTMX is known, but needs redefinition: %d\n",theDataTMX); + mxDestroyArray(theDataTMX); + theDataTMX = mxCreateDoubleMatrix(nVoxels,nSOM,mxREAL); + mexMakeArrayPersistent(theDataTMX); + } + } + + /* Now transpose it so we can look up time-series data for calculation of M.I. between + data and SOM */ + + mexCallMATLAB(1,&theDataTMX,1,&theDataMX,"'"); + theDataT = mxGetPr(theDataTMX); + for (iVoxel = 0; iVoxel < nVoxels; iVoxel++) + { + for (iSOM = 0; iSOM < nSOM; iSOM++) + { + results[nVoxels*iSOM+iVoxel] = SOM_MutualInformation(&theDataT[iVoxel*nTime],&SelfOMap[iSOM*nTime],nTime); +#if SOMDEBUG2 + mexPrintf("\n"); +#endif + } + } + break; + default: + SOM_CostFunctionUsage(); + mexErrMsgTxt("Error - whichCOST unknown"); + } +} + + +/*// All done.*/ + diff --git a/som/SOM_CostFunction.m b/som/SOM_CostFunction.m new file mode 100755 index 00000000..f6fe669d --- /dev/null +++ b/som/SOM_CostFunction.m @@ -0,0 +1,46 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005-2007 +% +% +% function results = SOM_CostFunction(slot,theSOM) +% +% IT IS ASSUMED THAT THE DATA AND SOM ARE UNIT NORMED +% +% global SOMMem +% SOMMem.theData = theData(nSpace,nTime); +% +% theSOM = theSOM(nTime,nSOM); +% +% idx = array of indices for best matching SOM vector. +% wts = cos(angle) or euclidean distance. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function SOM_CostFunction(slot,theSOM,CF); + +global SOMMem + +switch CF + case 0 + SOMMem{slot}.dataBySOM = SOMMem{slot}.theData*theSOM; + case 1 + SOMMem{slot}.dataBySOM = 2*(1.-SOMMem{slot}.theData*theSOM); + case 2 + SOMMem{slot}.dataBySOM = 4*(1.-SOMMem{slot}.theData*theSOM).^2; + case 3 + fprintf('Too slow to do mutual information at the moment !\n'); + SOMMem{slot}.dataBySOM = SOM_CostFunctionMI(SOMMem{slot}.theData,theSOM); + return +end + +% Free up memory - hopefully. + +clear theSOM + +return + +% +% All done. +% diff --git a/som/SOM_CostFunctionMI.c b/som/SOM_CostFunctionMI.c new file mode 100755 index 00000000..d1511369 --- /dev/null +++ b/som/SOM_CostFunctionMI.c @@ -0,0 +1,494 @@ +/*----------------- +% +% Copyright Robert C. Welsh, Ann Arbor, MI, 2006 +% +% A routine to calculate the Mutual Information Cost Metric +% between U and V, where U are data (nVoxels,nTime) and +% V is SOM Exemplars (nTime,nVoxels); +% +% results = SOM_CostFunctionMI(theData,SelfOMap); +% +% Input: +% +% theData = theData(nVoxels,nTime) +% SelfOMap = SelfOMap(nTime,nSOM); +% +% This will explicitly calculate the Mutual Information +% as the cost function. +% +% Output: +% +% results = results(nVoxels,nSOM) -> Cost Function. +% +% To compile do: +% +% mex [-DSOMDEBUG] [-DSOMDEBUG2] SOM_CostFunctionMI.c +% +% where -DSOMDEBUG[2] is a debug flag to the compiler. +% +% +% -Robert Welsh, 2006-12-12. +% +%------------------*/ + +#include +#include "mex.h" + + +#define EPS 1e-10 + +/* + Results area - make it global so we can access it for multiple calls. +*/ + +static mxArray *resultsMX=NULL; + +static int nPntsSaved=0; + +/*static mxArray *theDataTMX=NULL;*/ + + /* Dynamically create the histograms: joint, x, and y */ + +static double *JH=NULL, *HX=NULL, *HY=NULL; + +/* + Exit routine - need to use "clear SOM_CostFunction" to free up the memory. +*/ + +static void SOM_ExitCost(void) +{ + + mexPrintf("SOM_ExitCost has been called.\n"); + if (resultsMX != NULL) + { + mexPrintf("Destroying persistent\n"); + mxDestroyArray(resultsMX); + resultsMX = NULL; + } + /* + if (theDataTMX != NULL) + { + mexPrintf("Destroying persistent 'theDataTMX'\n"); + mxDestroyArray(theDataTMX); + theDataTMX = NULL; + } +*/ + if ( JH != NULL ) + { + mexPrintf("Destroying persistent 'JH'\n"); + mxFree(JH); + JH=NULL; + } + if ( HX != NULL ) + { + mexPrintf("Destroying persistent 'HX'\n"); + mxFree(HX); + HX=NULL; + } + if ( HY != NULL ) + { + mexPrintf("Destroying persistent 'HY'\n"); + mxFree(HY); + HY=NULL; + } +} + +/* + + Routine to calculate the Mutual Inforation Cost Function. + Based loosely on some code from Luis Hernandez. + + *** WARNING *** Mutual Information calculation is really slow!!! + +*/ + +double SOM_MutualInformation(double data[], double som[], int npnts) +{ + + int iPnt; + + double minx, maxx, deltax, miny, maxy, deltay; + + int nx, nxx; + + int iX, iY; + + double MI; + + double histIncr; + + /* Make sure we have enough data points to histogram */ + + if (npnts < 2) + { + mexPrintf("The number of times points < 2, silly.\n"); + mexErrMsgTxt("Aborting."); + }; + + /* Ok, determine limits of histograms for calculating entropy */ + + minx = 1e9; + miny = 1e9; + maxx = -1e9; + maxy = -1e9; + + /* Find the limits of the histograms. */ + + for (iPnt = 0;iPnt < npnts;iPnt++) + { + minx = ( (minxdata[iPnt]) ? maxx : data[iPnt] ); + maxy = ( (maxx>som[iPnt]) ? maxy : som[iPnt] ); + } + +#if SOMDEBUG3 + mexPrintf("%f %f %f %f\n",minx,maxx,miny,maxy); +#endif + + /* Determine number of elements in each histogram direction */ + + nx = (int) ( pow( (float)npnts , (float)(1./3.) ) + .5); + + deltax = (maxx-minx)/(nx-1); + deltay = (maxy-miny)/(nx-1); + + /* now add a bin for underflows and overflows */ + + nxx = nx + 2; + + if ( nx < 2 ) + { + mexPrintf("\nSOM_CostFunction.c:\n Histogram size needs to be at least 2x2\n\n"); + mexErrMsgTxt("Aborting."); + } + + + /* If previously created can be reuse? */ + + if ( nPntsSaved != npnts ) + { + if ( JH != NULL) mxFree(JH); + if ( HX != NULL) mxFree(HX); + if ( HY != NULL) mxFree(HY); + JH = NULL; + HX = NULL; + HY = NULL; + } + + /* Request memory from matlab heap for histograms */ + + if ( JH == NULL) + { + JH = mxCalloc((nxx*nxx),sizeof(double)); + mexMakeMemoryPersistent(JH); + } + if ( HX == NULL ) + { + HX = mxCalloc(nxx,sizeof(double)); + mexMakeMemoryPersistent(HX); + } + if ( HY == NULL ) + { + HY = mxCalloc(nxx,sizeof(double)); + mexMakeMemoryPersistent(HY); + } + + /* Accumulate the histograms, use the following + for the histogram increment, that way it will be + unit normalized already */ + + histIncr = (double) (1.0/( (double) npnts)); + + for (iPnt = 0; iPnt < npnts; iPnt ++) + { + /* Calculate index and min and max it.*/ + + iX = (int) ((data[iPnt]-minx)/deltax + 1.5); + iY = (int) ((som[iPnt]-miny)/deltay + 1.5); + +#if SOMDEBUG4 + mexPrintf("%+f:%d \t %+f:%d\n",data[iPnt],iX,som[iPnt],iY); +#endif + + iX = (iX < 0) ? 0 : iX; + iY = (iY < 0) ? 0 : iY; + + iX = (iX > nx+1) ? nx+1 : iX; + iY = (iY > nx+1) ? nx+1 : iY; + + HX[iX] += histIncr; + HY[iY] += histIncr; + JH[iX*nxx+iY] += histIncr; + } + +#if SOMDEBUG2 + for (iX = 0; iX <= nx+1; iX ++) + { + for (iY = 0; iY <= nx+1 ; iY++) + mexPrintf("%f ",JH[iX*nxx+iY]); + mexPrintf("\n"); + } + mexPrintf("\n"); +#endif + + /* Now calculate the entropy, include overflow and underflow bins */ + + MI = 0; + + for (iX = 0; iX < nxx; iX ++) + for (iY = 0; iY < nxx ; iY++) + if ( HX[iX] > 0 && HY[iY] > 0 ) + { +#if SOMDEBUG2 + mexPrintf("%f %f %f %d %d\n",JH[iX*nxx+iY],HX[iX],HY[iY],iX,iY); +#endif + MI += JH[iX*nxx+iY]*log( ( (JH[iX*nxx+iY]+EPS)/HX[iX]/HY[iY] ) ); + } + +#if SOMDEBUG2 + mexPrintf("min/max : %f %f %f\n",minx,maxx,deltax); + mexPrintf("min/max : %f %f %f\n",miny,maxy,deltay); + mexPrintf("\n"); +#endif + + /* Free up the memory */ + + /* mxFree(JH); + mxFree(HX); + mxFree(HY); + */ +#if SOMDEBUG2 + mexPrintf("MI : %f\n",MI); +#endif + + return MI; + +} + +void SOM_CostFunctionUsage() +{ + mexPrintf("\nUsage : results = SOM_CostFunctionMI(theData,SelfOMap)\n\n"); + mexPrintf(" theData -> theData(nVoxels,nTime)\n"); + mexPrintf(" SelfOMap -> theData(nTime,nSOM)\n"); +} + +/* + This is the main function that is called by MATLAB + + The routine will return a matrix of the cost-function evaluation. + + All of the cost functions are contained in here, except mutual information + which is lengthy code-wise and hence is above. + +*/ + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + mxArray *theDataTMX; + mxArray *theDataMX; + mxArray *SelfOMapMX; + + double *results; + + double *theData; + double *SelfOMap; + + double *theDataT; /* Needed for mutual information */ + + int nTime; + int nTimeSOM; + + int nSOM; + int nVoxels; + + int iSOM; + int iTime; + int iVoxel; + + int i1; + + int iPnt; + int COSTFLAG; + + double tmp1; + double tmp2; + + int idx1; + int idx2; + + /*// Now the function.*/ + + /* Register the exit function */ + + mexAtExit(SOM_ExitCost); + + /*// Did they pass in enough arguements?*/ + + if (nrhs != 2) + { + SOM_CostFunctionUsage(); + mexErrMsgTxt("Error, wrong number of input parameters."); + } + + /*// Get the pointers to the data and self-organizing map.*/ + + theDataMX = prhs[0]; + SelfOMapMX = prhs[1]; + + /*// Now get the dimensions of each.*/ + + nVoxels = mxGetM(theDataMX); + nTime = mxGetN(theDataMX); + + nTimeSOM = mxGetM(SelfOMapMX); + nSOM = mxGetN(SelfOMapMX); + + theData = mxGetPr(theDataMX); + + SelfOMap = mxGetPr(SelfOMapMX); + + /*// Make sure the dimensions are good.*/ + if (nTime != nTimeSOM) + { + SOM_CostFunctionUsage(); + mexPrintf("Error, time points in 'SelfOMap' and time points of 'theData' don't match!\n"); + mexErrMsgTxt("Aborting."); + }; + +#if SOMDEBUG + mexPrintf("theData(%d,%d), SelfOMap(%d,%d)\n",nVoxels,nTime,nTimeSOM,nSOM); + mexPrintf("number of returns : %d\n",nlhs); +#endif + + /*// Results returned in a new matrix.*/ + + if (resultsMX == NULL) + { + mexPrintf("resultsMX is unknown\n"); + resultsMX = mxCreateDoubleMatrix(nVoxels,nSOM,mxREAL); + mexMakeArrayPersistent(resultsMX); + mexPrintf("Created persistent resultsMX array.\n"); + } + else + { + /* Check it's size */ + if (mxGetM(resultsMX) != nVoxels || mxGetN(resultsMX) != nSOM) + { + mexPrintf("resultsMX is known, %d\n",resultsMX); + mexPrintf("but, wrong size and must destroy persistent array and recreate.\n"); + mxDestroyArray(resultsMX); + resultsMX = mxCreateDoubleMatrix(nVoxels,nSOM,mxREAL); + mexMakeArrayPersistent(resultsMX); + } + } + + plhs[0] = resultsMX; + +#if SOMDEBUG + mexPrintf("Created output array of size %d x %d\n",mxGetM(plhs[0]),mxGetN(plhs[0])); +#endif + + /*// get the pointer to the data area of the output array.*/ + + results = mxGetPr(plhs[0]); + + /*// Now run the cost function.*/ + + /* + Mutual Information. + */ +#if SOMDEBUG + mexPrintf("M.I.\n"); +#endif + /* Transpose the data matrix to be able to index it via c like indexing.*/ + + /* Does the transpose area already exist, if so the right size. If so just use + the allocated space. This helps memory management.*/ + + /* if (theDataTMX == NULL) + { + mexPrintf("theDataTMX is unknown\n"); + theDataTMX = mxCreateDoubleMatrix(nTime,nVoxels,mxREAL); + mexMakeArrayPersistent(theDataTMX); + mexPrintf("Created persistent theDataTMX(%d,%d) array.\n",mxGetM(theDataTMX),mxGetN(theDataTMX)); + } + else + { + if ( mxGetM(theDataTMX) != mxGetN(theDataMX) || mxGetN(theDataTMX) != mxGetM(theDataMX) ) + { + mexPrintf("theDataTMX (%d,%d) ~= (%d,%d)' is known, but needs redefinition: %d\n", + mxGetM(theDataTMX), + mxGetN(theDataTMX), + mxGetM(theDataMX), + mxGetN(theDataMX), + theDataTMX); + mxDestroyArray(theDataTMX); + mexPrintf("Destroyed.\n"); + theDataTMX = mxCreateDoubleMatrix(nTime,nVoxels,mxREAL); + mexPrintf("Created.\n"); + mexMakeArrayPersistent(theDataTMX); + mexPrintf("Made persistent.\n"); + } + } + + */ + /* Now transpose it so we can look up time-series data for calculation of M.I. between + data and SOM */ + + theDataTMX = mxCreateDoubleMatrix(nTime,nVoxels,mxREAL); + /*mexPrintf("Created theDataTMX(%d,%d) array.\n",mxGetM(theDataTMX),mxGetN(theDataTMX));*/ + + mexCallMATLAB(1,&theDataTMX,1,&theDataMX,"'"); + +#if SOMDEBUG + mexPrintf("Transposed with result of (%d,%d)\n",mxGetM(theDataTMX),mxGetN(theDataTMX)); +#endif + theDataT = mxGetPr(theDataTMX); + for (iVoxel = 0; iVoxel < nVoxels; iVoxel++) + { + for (iSOM = 0; iSOM < nSOM; iSOM++) + { + results[nVoxels*iSOM+iVoxel] = SOM_MutualInformation(&theDataT[iVoxel*nTime],&SelfOMap[iSOM*nTime],nTime); + +#if SOMDEBUG2 + mexPrintf("\n"); +#endif + + } + } +#if SOMDEBUG + mexPrintf("Finished calculation theDataTMX(%d,%d)\n",mxGetM(theDataTMX),mxGetN(theDataTMX)); +#endif + /* + mexPrintf("Destroying persistent 'theDataTMX'\n"); + mxDestroyArray(theDataTMX); + */ + theDataTMX = NULL; + + /* + if ( JH != NULL ) + { + mexPrintf("Destroying persistent 'JH'\n"); + mxFree(JH); + JH=NULL; + } + if ( HX != NULL ) + { + mexPrintf("Destroying persistent 'HX'\n"); + mxFree(HX); + HX=NULL; + } + if ( HY != NULL ) + { + mexPrintf("Destroying persistent 'HY'\n"); + mxFree(HY); + HY=NULL; + } + */ +} + + +/*// All done.*/ + diff --git a/som/SOM_CostFunctionMI.m b/som/SOM_CostFunctionMI.m new file mode 100755 index 00000000..f8eef983 --- /dev/null +++ b/som/SOM_CostFunctionMI.m @@ -0,0 +1,54 @@ +function results = SOM_CostFunctionMI(theData, theSOM) + +nVoxels = size(theData,1); +nTime = size(theData,2); +nSOM = size(theSOM,2); + +minx = min(theData,[],2); +miny = min(theSOM,[],1); +minx_rep = repmat(minx,1,nTime); +miny_rep = repmat(miny,nTime,1); +maxx = max(theData,[],2); +maxy = max(theSOM,[],1); + +nx = floor(nTime^(1/3)+.5); +deltax = (maxx-minx)/(nx-1); +deltay = (maxy-miny)/(nx-1); +deltax_rep = repmat(deltax,1,nTime); +deltay_rep = repmat(deltay,nTime,1); + +iX = floor((theData-minx_rep)./deltax_rep+1.5); +iY = floor((theSOM-miny_rep)./deltay_rep+1.5); + +histIncr = 1/nTime; + +HX = zeros(nVoxels,nx); +HY = zeros(nSOM,nx); +for ii=1:nx + HX(:,ii) = sum(iX==ii,2)*histIncr; + HY(:,ii) = sum(iY==ii,1)*histIncr; +end +clear deltax +clear deltax_rep +clear maxx +clear minx +clear minx_rep +%keyboard; +%tic +for ii=1:nx + eval(sprintf('HY_rep%d = log(repmat(HY(:,%d)'',nVoxels,1));',ii,ii)); +end +%HY_rep = log(repmat(HY(:,1)',nVoxels,1)); +results = zeros(nVoxels,nSOM); +for ii=1:nx + HX_rep = repmat(HX(:,ii),1,nSOM); + HX_rep = log(HX_rep); + for jj=1:nx + JH = single(iX==ii)*histIncr*(iY==jj); + eval(sprintf('results = results + JH.*(log((JH+1e-10))-HX_rep-HY_rep%d);',jj)); + end + ii +end +%toc + +%keyboard; \ No newline at end of file diff --git a/som/SOM_CreateMask.m b/som/SOM_CreateMask.m new file mode 100755 index 00000000..cffb9dc9 --- /dev/null +++ b/som/SOM_CreateMask.m @@ -0,0 +1,182 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% Routine to create analysis mask. +% +% function [hdr, results, analyzeFMT] = SOM_CreateMask(P) +% +% P - a list of files to create the mask from. +% +% hdr - Analyze hdr for the mask. +% results - a binary image of the mask +% analyzeFMT - a flag, 1 if analyze used, 0 if plain mat files. +% +% If you are using plain files, then make sure that the only +% item contained in the file is a single volume time point. +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [hdr, results, analyzeFMT] = SOM_CreateMask(P) + +global SOM + +if ~isfield(SOM,'maskThresh') + SOM.maskThresh = .125; +else + if SOM.maskThresh > 1; + SOM.maskThresh = 1/SOM.maskThresh; + fprintf('Making SOM.maskThresh < 1 : %f\n',SOM.maskThresh); + end +end + +fprintf('Create mask...\n'); + +% Determine whether to use spm or read in mat files? + +[fPath fName fExt] = fileparts(P(1,:)); + +if strcmp(strtrim(lower(fExt)),'.img') + analyzeFMT=1 +else + if (strcmp(strtrim(lower(fExt)),'.nii')) + analyzeFMT=2; + else + analyzeFMT=0; + end +end + +if analyzeFMT == 1 + som_mask = spm_read_vols(spm_vol(P(1,:))); +else + if analyzeFMT == 2 + mtx = spm_read_vols(spm_vol(P(1,:))); + som_mask = ones(size(mtx,1),size(mtx,2),size(mtx,3)); + else + tmpVol = load(P(1,:)); + fldNm = fieldnames(tmpVol); + if length(fldNm) ~= 1 + fprintf('\nError - the mat file has more than one variable.\n'); + fprintf('File : %s\n',P(1,:)); + hdr = []; + results = []; + analyzeFMT = []; + return + end + som_mask = getfield(tmpVol,fldNm{1}); + end +end +volSIZE = size(som_mask); + +som_mask = ones(size(som_mask)); + +spm('defaults','fmri'); +global defaults; + +if analyzeFMT == 2 + V = spm_vol(P(1,:)); + for iV = 1:size(V,1) + vol = spm_read_vols(V(iV)); + t(iV) = spm_global(V(iV)); + som_mask = som_mask.*(vol>(defaults.mask.thresh * t(iV))); + % + % Check to see if the volumes being read are all the same size. + % + if any(volSIZE - size(vol)) + fprintf('\nVolumes are of different size!.\n'); + fprintf('%s\n',P(iP,:)); + hdr = []; + results = []; + analyzeFMT= []; + return + end + clear vol; + end + +else + for iP = 1:size(P,1) + fprintf('\b\b\b%03d',iP) + if analyzeFMT == 1 + vol = spm_read_vols(spm_vol(P(iP,:))); + t(iP) = spm_global(spm_vol(P(iP,:))); + else + tmpVol = load(P(iP,:)); + fldNm = fieldnames(tmpVol); + if length(fldNm) ~= 1 + fprintf('\nError - the mat file has more than one variable.\n'); + fprintf('File : %s\n',P(iP,:)); + hdr = []; + results = []; + analyzeFMT = []; + return + end + vol = getfield(tmpVol,fldNm{1}); + mvol = mean(mean(mean(vol)))*SOM.maskThresh; % Just like SPM. + t(iP) = mean(vol(find(vol>mvol))); + + end + som_mask = som_mask.*(vol>(defaults.mask.thresh * t(iP))); + % + % Check to see if the volumes being read are all the same size. + % + if any(volSIZE - size(vol)) + fprintf('\nVolumes are of different size!.\n'); + fprintf('%s\n',P(iP,:)); + hdr = []; + results = []; + analyzeFMT= []; + return + end + clear vol; + end +end + + +fprintf('\b\b\bdone\n'); + +iMask = find(som_mask); + +if analyzeFMT == 1 + hdr = spm_vol(P(1,:)); + [pn fn] = fileparts(hdr.fname); + nhdr.fname = fullfile(pn,'som_mask.img'); + nhdr.dim(1:3) = hdr.dim(1:3); + nhdr.mat = hdr.mat; + nhdr.descrip = ['SOM Created Masked based on SPM:',spm('ver')]; + if strcmp(spm('ver'),'SPM5') | strcmp(spm('ver'),'SPM8') + nhdr.dt = [4 0]; + else + nhdr.dim(4) = 4; + end + spm_write_vol(nhdr,som_mask); + hdr = nhdr; +else + if analyzeFMT == 2 + hdr = spm_vol(P(1,:)); + hdr = hdr(1); + [pn fn] = fileparts(hdr.fname); + nhdr.fname = fullfile(pn,'som_mask.img'); + nhdr.dim(1:3) = hdr.dim(1:3); + nhdr.mat = hdr.mat; + nhdr.descrip = ['SOM Created Masked based on SPM:',spm('ver')]; + if (strcmp(spm('ver'),'SPM5') | strcmp(spm('ver'),'SPM8')) + nhdr.dt = [4 0]; + else + nhdr.dim(4) = 4; + end + spm_write_vol(nhdr,som_mask); + hdr = nhdr; + else + + hdr = []; + save(fullfile(fPath,'som_mask'),'som_mask'); + end +end + +results = som_mask; + +clear som_mask; + +% +% All done. +% diff --git a/som/SOM_Detrend.m b/som/SOM_Detrend.m new file mode 100644 index 00000000..e726fe04 --- /dev/null +++ b/som/SOM_Detrend.m @@ -0,0 +1,65 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% +% newData = SOM_Detrend(data,polyorder); +% +% +% Input Parameters that we need for preparing the data +% +% polyorder = order of the polynomial, 0 is mean centered. +% +% data = data (space x time); +% +% This we operate on the 2nd dimension. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_Detrend(Y,polyorder) + +% Check to see if they specified the polynomial order. + +if nargin == 1 + polyorder = 0; +end + +% +% If polyorder = 0 then we are just mean centering the data. +% +% Okay they specified something, and actually we only recommend +% linear detrending due to the definition of zero in time. +% + +X = zeros(polyorder+1,size(Y,2)); + +% This handles the mean. + +X(1,:) = 1; + +% Now the other terms. + +for iP = 1:polyorder + X(iP+1,:) = ([1:size(X,2)].^iP); +end + +% We solve the GLM that is written in matrix form as +% +% Y = Beta X +% +% Beta = space x regressor +% X = regressor x time +% +% Beta = Y * pinv(X) +% +% and thus +% +% Yp = Y - Beta X = Y - ( Y pinv(X) ) X +% + +results = Y - ( Y * pinv(X) ) * X; + +return diff --git a/som/SOM_Ex2SC.m b/som/SOM_Ex2SC.m new file mode 100755 index 00000000..2392bcda --- /dev/null +++ b/som/SOM_Ex2SC.m @@ -0,0 +1,66 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2007 +% +% A routine to remap the indices of the voxel solutions from +% the exemplars in the full SOM to the exemplars of the +% super clustered SOM +% +% IDX = SOM_Ex2SC(SOMResults.IDX,SCResults.IDX) +% +% Input : +% +% SOMResults.IDX - the indices pointing to the exemplars +% SCResults.IDX - indices of exemplars to super-cluster +% exemplars. +% +% Output : +% +% IDX - pointers to super-cluster exemplars for +% for each voxel. +% +% +% You would use this for datamining based on ROI's. Operationally, +% you would troll the data space looking for the super-cluster +% that occupied your ROI, then you would display that super-cluster +% exemplar cost-function, or rho-value or z-map. +% +% See SOM_CostFunction +% SOM_Pearson +% SOM_Rho2Z +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function IDX = SOM_Ex2SC(IDX1,IDX2) + +IDX = []; + +% +% Make sure things are in range. +% + +nExemplars = size(IDX2); + +maxIDX = max(IDX1); + +if maxIDX > nExemplars + fprintf('The exemplar indices are bigger than the number of exemplars in the super-clusters.\n'); + return +end + +% +% Now do the remapping. +% + +IDX = 0*IDX1; + +for ii = 1:length(IDX2) + IDX(find(IDX1==ii)) = IDX2(ii); +end + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_FTest.m b/som/SOM_FTest.m new file mode 100755 index 00000000..1408e7b3 --- /dev/null +++ b/som/SOM_FTest.m @@ -0,0 +1,62 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2007 +% +% +% A routine to calculate a two-sample F-Test. +% +% [FMap Nu] = SOM_Fest(DataSample1,DataSample2); +% +% Input : +% +% DataSample1 = DataSample1(nVoxels,nSubjects1) +% DataSample2 = DataSample2(nVoxels,nSubjects2) +% +% Output: +% +% FMap = TMap(nVoxels) +% Nu - Number of degree of freedom. +% +% The F-Test is calculated such that is an upper one tailed. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [FMap Nu] = SOM_FTest(DataSample1,DataSample2) + +FMap = []; +Nu = []; + +% Get the first group information. + +nVoxels1 = size(DataSample1,1); +nSubject1 = size(DataSample1,2); + +% Is there a second group available. + +nVoxels2 = size(DataSample2,1); +nSubject2 = size(DataSample2,2); + +if any ( [nVoxels1] - [nVoxels2] ) + fprintf('Error, you have specified data arrays with different number of voxels.\n'); + return +end + +if nSubject2 < 2 | nSubject1 < 2 + fprintf('You need to have at least 2 subjects per group.\n'); + return +end + +G1Var = var(DataSample1,[],2); +G2Var = var(DataSample2,[],2); + +G1Mu = mean(DataSample1,2); +G2Mu = mean(DataSample2,2); + +FMap = max([G1Var G2Var],[],2)./min([G1Var G2Var],[],2); + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_Filter.m b/som/SOM_Filter.m new file mode 100755 index 00000000..3e9d8c78 --- /dev/null +++ b/som/SOM_Filter.m @@ -0,0 +1,233 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% +% A routine to bandpass filter data +% +% theData = theData(space,time) (this is the +% standard format being used in this SOM +% implementation). +% +% sample = sample period (TR in fmri language) +% +% lowFreq = low frequency cutoff +% +% highFreq = high frequency cutoff +% +% gentle = 0 - no just a hard cut, 1 - yes. +% gentle is a curve of 0.1, .5, .9, 1.0 on the rising and +% falling edges except for DC. +% = 0 to use fir2 when using the Signal Processing ToolBox +% 1 to use firpm +% +% padding = padding for filtering. (default is 10) +% +% whichFilter = 0 try to use TBX, else use SOM_Filter_FFT +% +% function [results, b, filtParms] = SOM_Filter(theData,sample,lowFreq,highFreq,gentle,padding,whichFilter) +% +% +% If the signal toolbox is present then use that to do the +% filtering. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results, b, filtParms] = SOM_Filter(theData,sample,lowFreq,highFreq,gentle,padding,whichFilter) + +% Save input for posterity. + +filtParms.sample = sample; +filtParms.lowFreq = lowFreq; +filtParms.highFreq = highFreq; +filtParms.gentle = gentle; +filtParms.padding = padding; +filtParms.whichFilter = whichFilter; + +% make sure padding exists. + +if exist('padding') == 0 + SOM_LOG('STATUS : Default padding being set to 10.'); + padding = 10; +end + +% make sure "whichFilter" is existing. + +if exist('whichFilter') == 0 + SOM_LOG('STATUS : Default to trying to use Signal Processing Toolbox.'); + whichFilter = 0; +end + +% If padding is on then we need to pad the beginning and the end +% by "padding" number of average images. These are then +% removed later. + +% Make sure padding is integer and > 0. + +padding = floor(padding); + +if padding < 0 + padding = 0; +end + +theDataMean = mean(theData,2); + +theDataPad = repmat(theDataMean,1,padding); + +theData = [theDataPad theData theDataPad]; + +% Determine if firpm is present and filtfilt if whichFilter == 0 +% Get the fft of the data. + +if whichFilter + SOM_LOG('INFO : Using handmade fft filter method.'); + [results b] = SOM_Filter_FFT(theData,sample,lowFreq,highFreq,gentle); +elseif exist('firpm.m') == 2 & exist('filtfilt.m') == 2 & whichFilter == 0 + SOM_LOG('INFO : Using Signal Processing ToolBox'); + [results b] = SOM_Filter_SIGTBX(theData,sample,lowFreq,highFreq,gentle); +else + SOM_LOG('INFO : Using handmade fft filter method.'); + [results b] = SOM_Filter_FFT(theData,sample,lowFreq,highFreq,gentle); +end + +% Now trim out the padding; + +results = results(:,1+padding:end-padding); + +return + +% +% Using the Signal Processing ToolBox +% + +function [results, b] = SOM_Filter_SIGTBX(theData,sample,lowFreq,highFreq,firOpt); + +%Determine the Nyquist criterion + +nyquist = 1/sample/2; + +% How many time points. + +N = size(theData,2); + +nFilt = floor(N/3)-1; + +if nFilt < 1 + SOM_LOG('FATAL ERROR : insufficient data to actually filter.'); + results = []; + b = []; + return +end + +% Need to put in protection for +% 1) lowFreq <=0 and/or +% 2) highFreq >=nyquist. + +if lowFreq > 0 + fir2Freq = [0 lowFreq/nyquist*.999 lowFreq/nyquist ]; + fir2coef = [0 0 1 ]; +else + fir2Freq = [0 ]; + fir2coef = [1 ]; +end + +if highFreq < nyquist + fir2Freq = [fir2Freq highFreq/nyquist highFreq/nyquist*1.001 1.0 ]; + fir2coef = [fir2coef 1 0 0 ]; +else + fir2Freq = [fir2Freq 1.0 ]; + fir2coef = [fir2coef 1 ]; +end + +if firOpt == 0 + b = fir2(nFilt,[0 lowFreq/nyquist*.999 lowFreq/nyquist highFreq/nyquist ... + highFreq/nyquist*1.001 1.0],[0 0 1 1 0 0]); +else + b = firpm(nFilt,[0 lowFreq/nyquist*.999 lowFreq/nyquist highFreq/nyquist ... + highFreq/nyquist*1.001 1.0],[0 0 1 1 0 0]); +end + +% Filter the data now, forwards and backwards, using +% the transpose of data as time flows down, not across. + +results = (filtfilt(b,1,theData'))'; + +return + + +% +% Using the FFT Method. Problem with Phase? +% + +function [results, b] = SOM_Filter_FFT(theData,sample,lowFreq,highFreq,gentle) + + +%Determine the Nyquist criterion + +nyquist = 1/sample/2; + +% How big is our sample. + +N = size(theData,2); + +% Make a frequency baseline. + +deltaF = nyquist/(floor(N/2)-1); + +freq = (-floor(N/2):floor(N/2)-1)*deltaF; + +% Get the fft of the data. + +ffttheData = fftshift(fft(theData,[],2),2); + +% Find what to remove. + +fftMask = zeros(size(theData)); +maskH = zeros(size(theData)); +maskL = zeros(size(theData)); + +% Need to put in protection for +% 1) lowFreq <=0 and/or +% 2) highFreq >=nyquist. + +ifrqHi = find(abs(freq)<=highFreq); +ifrqLo = find(abs(freq)>=lowFreq); + +maskH(:,ifrqHi) = 1; +maskL(:,ifrqLo) = 1; + +fftMask = maskH.*maskL; + +% Find the transition points and roll a little. + +iup = find(diff(fftMask(1,:))>0); +idn = find(diff(fftMask(1,:))<0); + +if gentle ~= 0 + fftMask(:,iup-1) = .1; + fftMask(:,iup) = .5; + fftMask(:,iup+1) = .9; + fftMask(:,idn+2) = .1; + fftMask(:,idn+1) = .5; + fftMask(:,idn) = .9; +end + +% DC Component is saved. + +dci = floor(N/2)+1; + +fftMask(:,dci) = 1; + +% Filter and return the results - returning the real component. + +results = real(ifft(ifftshift(fftMask.*ffttheData,2),[],2)); + +b = fftMask; + +return + +% +% All done. +% + diff --git a/som/SOM_FindClosest.m b/som/SOM_FindClosest.m new file mode 100755 index 00000000..cfc4df13 --- /dev/null +++ b/som/SOM_FindClosest.m @@ -0,0 +1,112 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005-2007 +% +% +% function results = SOM_FindClosest(theSOM) +% +% IT IS ASSUMED THAT THE DATA AND SOM ARE UNIT NORMED +% +% global SOMMem +% SOMMem.theData = theData(nSpace,nTime); +% +% theSOM = theSOM(nTime,nSOM); +% +% idx = array of indices for best matching SOM vector. +% wts = cos(angle) or euclidean distance. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [idx, wts, didx, dwts] = SOM_FindClosest(theSOM,slot) + +global SOM + +global SOMMem + +idx = []; +wts = []; +didx = []; +dwts = []; + +% make sure data is present in SOMMem + +if isfield(SOMMem{slot},'theData') == 0 + fprintf('Data is missing from SOMMem\n'); + return +end + +% Pre-allocate the dataBySOM, but storing it into SOM? + +if isfield(SOMMem{slot},'dataBySOM') + % Correct size? + if prod(size(SOMMem{slot}.dataBySOM) - [size(SOMMem{slot}.theData,1) size(theSOM,2)]) ~= 0 + SOMMem{slot}.dataBySOM = zeros(size(SOMMem{slot}.theData,1),size(theSOM,2)); + end +else + SOMMem{slot}.dataBySOM = zeros(size(SOMMem{slot}.theData,1),size(theSOM,2)); +end + +% Cosing(opening angle) or Euclidean Distance. + +% SOMMem{slot}.dataBySOM = SOM_CostFunction(slot,theSOM,SOM.Cost); + +% Call to SOM_CostFunction - changed, SOMMem{slot}.dataBySOM is +% filled automatically by the call. + +SOM_CostFunction(slot,theSOM,SOM.Cost); + +% Place the sorted into global to help with memory issues? + +% Think about chunking this to cut down on memory load, however, we +% would need to be careful about chunk sizes to make sure we are +% getting into redefining chunck size all of the time. + +if SOM.Cost == 0 | SOM.Cost == 3 + [SOMMem{slot}.wts SOMMem{slot}.idx] = max(SOMMem{slot}.dataBySOM,[],2); + [SOMMem{slot}.dwts SOMMem{slot}.didx] = min(SOMMem{slot}.dataBySOM,[],2); +else + [SOMMem{slot}.dwts SOMMem{slot}.didx] = max(SOMMem{slot}.dataBySOM,[],2); + [SOMMem{slot}.wts SOMMem{slot}.idx] = min(SOMMem{slot}.dataBySOM,[],2); +end + +idx = SOMMem{slot}.idx; +wts = SOMMem{slot}.wts; +didx = SOMMem{slot}.didx; +dwts = SOMMem{slot}.dwts; + +% Find the index to the closest. If cost-function = 0 +% then the closest is given by biggest number. +% If the cost-function = 1 (Euclidean distance), then +% closest is by smallest number. All other cost will use the mex +% code for calculating U.V + +% Find the smallest - which if greatly negative would be deactivation or +% decorrleation? (This is not valid for Euclidean Distance as that +% is a positive definite metric, but fill up anyway. + +% $$$ if SOM.Cost == 0 | SOM.Cost == 3 +% $$$ % Smallest opening angle and mutual information +% $$$ idx = SOMMem{slot}.idx(:,end); +% $$$ wts = SOMMem{slot}.y(:,end); +% $$$ % largest angle, least mutual information. +% $$$ didx = SOMMem{slot}.idx(:,1); +% $$$ dwts = SOMMem{slot}.y(:,1); +% $$$ else +% $$$ % smallest euclidean distance and E.D squared. +% $$$ idx = SOMMem{slot}.idx(:,1); +% $$$ wts = SOMMem{slot}.y(:,1); +% $$$ % largest seperation +% $$$ didx = SOMMem{slot}.idx(:,end); +% $$$ dwts = SOMMem{slot}.y(:,end); +% $$$ end + +% Free up memory - hopefully. + +clear theSOM + +return + +% +% All done. +% diff --git a/som/SOM_FindSCMembers.m b/som/SOM_FindSCMembers.m new file mode 100755 index 00000000..ee374dd8 --- /dev/null +++ b/som/SOM_FindSCMembers.m @@ -0,0 +1,41 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2007 +% +% +% A routine to return indices of SelfOMap exemplars +% that belong to the same supercluster as the passed index. +% +% function [results scnum] = SOM_FindSCMembers(ExemplarIDX,SCIDX) +% +% Input : +% +% ExemplarIDX = Index into SelfOMap (0<#<=nSom) +% +% SCIDX = Index map of the supercluster membership. +% +% Output: +% +% results = list of exemplars that belong to same supercluster +% scnum = supercluster number. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results scnum] = SOM_FindSCMembers(ExemplarIDX,SCIDX) + +if ExemplarIDX < 1 || ExemplarIDX > length(SCIDX) + fprintf('ExemplarIDX is out of range.\n'); + results = []; + return +end + +results = find(SCIDX==SCIDX(ExemplarIDX)); + +scnum = SCIDX(ExemplarIDX); + +return + +% +% All done. +% diff --git a/som/SOM_FitGaussian.m b/som/SOM_FitGaussian.m new file mode 100755 index 00000000..f3b3156b --- /dev/null +++ b/som/SOM_FitGaussian.m @@ -0,0 +1,57 @@ +% +% A function that will take in a list of parameters +% as well as look at global memory and see if there +% is a minimization to perform +% +% function chi2 = SOM_Gaussian(parameters) +% +% fit a variable number of gaussians. + +function chi2 = SOM_FitGaussian(parameters) + +global SOMGaussian + +SOMGaussian.Parms = parameters; + +% Determine which elements of data to use. Only those with errors > +% 0 + +iIncl = find(SOMGaussian.Ye>0); + +SOMGaussian.nDF = length(iIncl); + +% If there are only two parameters then force mu=0; + +if length(parameters) > 2 + mus = parameters(1); + amps = parameters(2); + sigmas = parameters(3); +else + mus = 0; + amps = parameters(1); + sigmas = parameters(2); +end + +gausses = 0; + +SOMGaussian.Yth = gaussian(SOMGaussian.X,mus,sigmas,amps); + +SOMGaussian.Residuals = SOMGaussian.Yth - SOMGaussian.Y; + +chi2 = sum((SOMGaussian.Residuals(iIncl).^2)./(SOMGaussian.Ye(iIncl).^2)); + +% +% Return +% + +%function retGauss = gaussian(xVals,mu,sigma,amplitude) + +function retGauss = gaussian(xVals,mu,sigma,amplitude) + +retGauss = exp(-.5*((xVals-mu)/sigma).^2); + +retGauss = retGauss/max(retGauss)*amplitude; + +% +% all done +% diff --git a/som/SOM_Fix.m b/som/SOM_Fix.m new file mode 100755 index 00000000..bb084458 --- /dev/null +++ b/som/SOM_Fix.m @@ -0,0 +1,27 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% Ann Arbor MI. +% +% function results = SOM_Fix(SOMResults,maskName) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_Fix(SOMResults,maskName) + +maskHDR = spm_vol(maskName); + +maskVOL = spm_read_vols(maskHDR); + +SOMResults.iMask = find(maskVOL); + +SOMResults.header = maskHDR; + +results = SOMResults; + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_Gaussian.m b/som/SOM_Gaussian.m new file mode 100755 index 00000000..4c55b05b --- /dev/null +++ b/som/SOM_Gaussian.m @@ -0,0 +1,57 @@ +% +% A function that will take in a list of parameters +% as well as look at global memory and see if there +% is a minimization to perform +% +% function chi2 = SOM_Gaussian(parameters) +% +% fit a variable number of gaussians. + +function chi2 = SOM_Gaussian(parameters) + +global SOMGaussian + +SOMGaussian.Parms = parameters; + +% Determine which elements of data to use. Only those with errors > +% 0 + +iIncl = find(SOMGaussian.Ye>0); + +SOMGaussian.nDF = length(iIncl); + +% If there are only two parameters then force mu=0; + +if length(parameters) > 2 + mus = parameters(1); + amps = parameters(2); + sigmas = parameters(3); +else + mus = 0; + amps = parameters(1); + sigmas = parameters(2); +end + +gausses = 0; + +SOMGaussian.Yth = gaussian(SOMGaussian.X,mus,sigmas,amps); + +SOMGaussian.Residuals = SOMGaussian.Yth - SOMGaussian.Y; + +chi2 = sum((SOMGaussian.Residuals(iIncl).^2)./(SOMGaussian.Ye(iIncl).^2)); + +% +% Return +% + +%function retGauss = gaussian(xVals,mu,sigma,amplitude) + +function retGauss = gaussian(xVals,mu,sigma,amplitude) + +retGauss = exp(-.5*((xVals-mu)/sigma).^2); + +retGauss = retGauss/max(retGauss)*amplitude; + +% +% all done +% diff --git a/som/SOM_GlobalCalc.m b/som/SOM_GlobalCalc.m new file mode 100755 index 00000000..08efaf29 --- /dev/null +++ b/som/SOM_GlobalCalc.m @@ -0,0 +1,34 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% +% A program to return the global signal +% from a set of time series data in the SOM +% data analysis. +% +% (You could do this yourself, but just trying to make +% code pretty). +% +% function results = SOM_GlobalCalc(theData) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_GlobalCalc(theData) + +% Find the mean. + +results = mean(theData,1); + +% Now take the baseline off of the mean. + +results = results - mean(results); + +results = results'; + +return + +% +% All done. +% diff --git a/som/SOM_Grid4SCing.m b/som/SOM_Grid4SCing.m new file mode 100755 index 00000000..f93c1723 --- /dev/null +++ b/som/SOM_Grid4SCing.m @@ -0,0 +1,32 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2010 +% +% A function to take the Time x Space Exemplar map +% and return as a xSpace x ySpace x Time Exemplar map +% +% +% function OutMap = SOM_Grid4SC(InSelfOMap,xGrid,yGrid) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function OutMap = SOM_Grid4SC(InSelfOMap,xGrid,yGrid) + +nTime = size(InSelfOMap,1); + +nSpace = xGrid*yGrid; + +if nSpace ~= size(InSelfOMap,2) + fprintf('Error, size mismatch. InSelfOMap : %d x %d\n',size(InSelfOMap)); + OutMap = []; + return +end + +OutMap = reshape(InSelfOMap,[nTime xGrid yGrid]); + +OutMap = permute(OutMap,[2 3 1]); + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_GridSC.m b/som/SOM_GridSC.m new file mode 100755 index 00000000..737c5f4f --- /dev/null +++ b/som/SOM_GridSC.m @@ -0,0 +1,42 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2006 +% +% +% function results = SOM_GridSC(SOMSCResults) +% +% Input: +% +% SOMSCResults - results structure returned by SOM_SuperClsuterEasy +% +% Output: +% +% results - a matrix showing the supercluster membership for the +% SOM. +% +%- - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +function results = SOM_GridSC(SOMSCResults) + +results = []; + +nGrid = floor(sqrt(length(SOMSCResults.IDX))); + +if nGrid ~= sqrt(length(SOMSCResults.IDX)) + fprintf('Something wrong with nGrid.\n'); + return +end + +results = zeros(nGrid,nGrid); + +for iIDX = 1:max(SOMSCResults.IDX) + results(find(SOMSCResults.IDX == iIDX)) = iIDX; +end + +return + +% +% All done +% diff --git a/som/SOM_HowTo.key b/som/SOM_HowTo.key new file mode 100644 index 00000000..e2befa7f Binary files /dev/null and b/som/SOM_HowTo.key differ diff --git a/som/SOM_HowTo.pdf b/som/SOM_HowTo.pdf new file mode 100644 index 00000000..a9f5611b Binary files /dev/null and b/som/SOM_HowTo.pdf differ diff --git a/som/SOM_HowTo_MakingROIS.pdf b/som/SOM_HowTo_MakingROIS.pdf new file mode 100644 index 00000000..4e3784ec Binary files /dev/null and b/som/SOM_HowTo_MakingROIS.pdf differ diff --git a/som/SOM_InOutComponent.m b/som/SOM_InOutComponent.m new file mode 100755 index 00000000..b986cdae --- /dev/null +++ b/som/SOM_InOutComponent.m @@ -0,0 +1,58 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2010 +% +% Find the SOM component that best matches the template being +% passed. +% +% function results = SOM_InOutComponent(BinaryConnectivityMap,TheTemplate) +% +% BinaryConnectivityMap = the binarized connectivity map, +% 1 = connection +% 0 = none. +% +% TheTemplate = masking image with ROI. +% +% results = the count of the voxels in and out of the ROI, but containted to the brain. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_InOutComponent(BinaryConnectivityMap,TheTemplate) + +global SOMMem + +results = []; + +if isfield(SOMMem{1},'maskInfo') == 0 + return +end + +maskDim = size(TheTemplate); + +if sum(any(size(TheTemplate) - SOMMem{1}.maskInfo.size)) ~= 0 + fprintf('Masking template doesn''t match the size of the original data.\n'); + return +end + +BrainMask = zeros(SOMMem{1}.maskInfo.size); + +BrainMask(SOMMem{1}.maskInfo.iMask) = 1; + +NTheTemplate = TheTemplate .* BrainMask; + +ROIRemoved = sum(TheTemplate(:)-NTheTemplate(:)); + +NotTemplate = BrainMask.*(1 - NTheTemplate); + +InCount = sum(BinaryConnectivityMap(find(NTheTemplate))); +OutCount = sum(BinaryConnectivityMap(find(NotTemplate))); + +results = [InCount OutCount ROIRemoved]; + +return + +% +% All done. +% + diff --git a/som/SOM_LOG.m b/som/SOM_LOG.m new file mode 100755 index 00000000..5cebfc45 --- /dev/null +++ b/som/SOM_LOG.m @@ -0,0 +1,106 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% SOM_LOG +% +% Log output to the screen and to a text heap +% +% logStr = SOM_LOG('literal string'); +% +% If you want to access the log put the following code into your +% own function +% +% global SOM +% +% the log is then accessible via +% +% SOM.LOG +% +% We have multiple logging levels +% +% FATAL -- always written +% WARNING -- only if SOM.silent = 2 +% STATUS -- only if SOM.silent = 1 +% INFO -- really boring stuff only if SOM.silent == 0 +% [unknown] -- if we don't find one of the words above then we always +% write. +% +% unknown INFO STATUS WARNING FATAL +% SOM.silent +% +% 0 yes yes yes yes yes +% 1 yes no yes yes yes +% 2 yes no no yes yes +% 3 yes no no no yes +% +% We scan for the word in the first part of the passed string and make a +% guess. This is a bit loose, but good enough. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function logStr = SOM_LOG(inputLog) + +global SOM + +LOGLEVELS = {'INFO','STATUS','WARNING','FATAL'}; + +[ST STI] = dbstack; + +% The logic of the combination of log type and logging level. + +LOGOUTMATRIX = [ + 1 1 1 1 1; + 1 0 1 1 1; + 1 0 0 1 1; + 1 0 0 0 1]; + +% Initialize the logging level if necessary. + +if ~isfield(SOM,'silent') + SOM.silent = 0; +end + +if SOM.silent < 0 + SOM.silent = 0 +end + +if SOM.silent > 3 + SOM.silent = 3; +end + +% Always at the beginning of the log we write out that we are beginning the +% log. + +if isfield(SOM,'LOG') == 0 + SOM.LOG = []; + tmp = SOM.silent; + SOM.silent = 0; + SOM_LOG('INFO -- Starting LOGGING'); + SOM.silent = tmp +end + +inputLog = sprintf('%d:%02d:%02d:%02d:%02d:%02d : %30s/%04d : %s\n',fix(clock),ST(min([2 length(ST)])).name,ST(min([2 length(ST)])).line,inputLog); + +THISLEVEL = 1; + +for iLEVEL = 1:4 + if length(findstr(LOGLEVELS{iLEVEL},upper(inputLog))) > 0 + THISLEVEL = iLEVEL+1; + end +end + +SOM.LOG = strvcat(SOM.LOG,inputLog); + +% If the combination indicates writting out then we do. + +if LOGOUTMATRIX(SOM.silent+1,THISLEVEL) + fprintf(inputLog) +end + +logStr = SOM.LOG; + +return diff --git a/som/SOM_MakeGrid.m b/som/SOM_MakeGrid.m new file mode 100644 index 00000000..9064a0ad --- /dev/null +++ b/som/SOM_MakeGrid.m @@ -0,0 +1,76 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Routine to make a grid of rois. +% +% function results = SOM_MakeGrid(gridSpacing,[BB]) +% +% gridSpacing = the grid spacing in mm +% +% BB = the bounding box, optional. +% X, Y, Z as in defaults.normalise.write.bb from SPM8 +% +% +% Typically you,d call SOM_MakeGrid and then after you get your list of +% candidate locations you can pass to "SOM_roiPointsInMask" if you want to +% mask for gray matter. +% +% The grid created will straddle the left-right hemispheric fissure, but +% will start at Y=0 and Z=0. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_MakeGrid(gridSpacing,BB) + +% Make sure grid is > 0 + +if gridSpacing < 0 + SOM_LOG('FATAL : Grid spacing must be > 0'); + results = -1; + return +end + +% Did they define a bouding box> +if exist('BB') == 0 + BB = [-76, -112, -50; 76, 73, 84]; +end + +% Build the grid. +xvals1 = [gridSpacing/2:gridSpacing:BB(2,1)]; +xvals2 = [-gridSpacing/2:-gridSpacing:BB(1,1)]; +xvals = sort([xvals1 xvals2]); + +yvals1 = [0:gridSpacing:BB(2,2)]; +yvals2 = [-gridSpacing:-gridSpacing:BB(1,2)]; +yvals = sort([yvals1 yvals2]); + +zvals1 = [0:gridSpacing:BB(2,3)]; +zvals2 = [-gridSpacing:-gridSpacing:BB(1,3)]; +zvals = sort([zvals1 zvals2]); + +xgrid = repmat(xvals,[length(yvals) 1]); +ygrid = repmat(yvals',[1 length(xvals)]); + +mni_xcoords = zeros([size(xgrid) length(zvals)]); +mni_ycoords = zeros([size(xgrid) length(zvals)]); +mni_zcoords = zeros([size(xgrid) length(zvals)]); + +for iZ = 1:length(zvals) + mni_xcoords(:,:,iZ) = xgrid; + mni_ycoords(:,:,iZ) = ygrid; + mni_zcoords(:,:,iZ) = zvals(iZ); +end + +mni_coords_cand = [mni_xcoords(:) mni_ycoords(:) mni_zcoords(:) ]; + +results = mni_coords_cand; + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_MakeROI.m b/som/SOM_MakeROI.m new file mode 100755 index 00000000..fb6fd20b --- /dev/null +++ b/som/SOM_MakeROI.m @@ -0,0 +1,43 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% Take a coordinate and expand an ROI. +% +% function results = SOM_MakeROI(xyz,theSize,theLimits) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_MakeROI(xyz,theSize,theLimits) + +% +% +% + +results = []; + +xmin = floor((theSize(1)-1)/2); +xmax = floor((theSize(1))/2); +ymin = floor((theSize(2)-1)/2); +ymax = floor((theSize(2))/2); +zmin = floor((theSize(3)-1)/2); +zmax = floor((theSize(3))/2); + +fprintf('Expanding : %d %d %d\n',xyz); +fprintf(' to ROI defined by %d:%d, %d:%d, %d:%d\n',... + max([1 xyz(1)-xmin]),min([xyz(1)+xmax theLimits(1)]),... + max([1 xyz(2)-ymin]),min([xyz(2)+ymax theLimits(2)]),... + max([1 xyz(3)-zmin]),min([xyz(3)+zmax theLimits(3)])); + +for ix = max([1 xyz(1)-xmin]):min([xyz(1)+xmax theLimits(1)]) + for iy = max([1 xyz(2)-ymin]):min([xyz(2)+ymax theLimits(2)]) + for iz = max([1 xyz(3)-zmin]):min([xyz(3)+zmax theLimits(3)]) + results = [results; ix iy iz]; + end + end +end + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_MakeSphereROI.m b/som/SOM_MakeSphereROI.m new file mode 100755 index 00000000..2a7cd2a9 --- /dev/null +++ b/som/SOM_MakeSphereROI.m @@ -0,0 +1,49 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% +% A function to make a three-d sphere of radius R +% in voxel coordinates. +% +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_MakeSpereROI(R) + +Rbox = round(R); + +% Default is a single VOXEL. +if R < 1 + results = [0; 0; 0]; + return +end + +Xs = [-Rbox:Rbox]; +Ys = [-Rbox:Rbox]; +Zs = [-Rbox:Rbox]; + +XGrid = repmat(Xs,[length(Ys) 1]); +YGrid = repmat(Ys',[1 length(Xs)]); + +results = []; + +% Now loop on the Z's and find out if in the radius. + +for iZ = 1:length(Zs) + rDist = sqrt(XGrid(:).^2 + YGrid(:).^2 + Zs(iZ)^2); + RIDX = find(R>=rDist); + results = [results; XGrid(RIDX) YGrid(RIDX) Zs(iZ)*ones(length(RIDX),1)]; +end + +%Return as +% X +% Y +% Z + +results = results'; + +return diff --git a/som/SOM_MaskData.m b/som/SOM_MaskData.m new file mode 100755 index 00000000..185b5505 --- /dev/null +++ b/som/SOM_MaskData.m @@ -0,0 +1,110 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2006-2007 +% +% Routine to extract time-series from 4D using 3D mask. +% +% function [results, maskInfo] = SOM_MaskData(4d-data,3d-mask,[othervoxels]); +% +% Input : +% +% FourDData - X * Y * Z * t +% ThreeDMask - X * Y * Z (binary image) +% +% otherVoxels - See SOM_PrepData. +% +% Oupput : +% +% results - (space x time) +% maskInfo - see SOM_PrepData. +% +% Only those voxels that are included in the mask are read. +% +% NOTE : Eventually this needs to support NIFTI (nii) files. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results maskInfo] = SOM_MaskData(FourDData,ThreeDMask,otherVoxels); + +% Did they pass any requests for +% specific voxels to be extracted? + +if exist('otherVoxels') ~= 1 + otherVoxels = []; +end + +[xd yd zd nTime] = size(FourDData); + +[xdm ydm zdm] = size(ThreeDMask); + +if any([xdm ydm zdm]-[xd yd zd]) + fprintf('Data size doesn''t match mask size.\n'); + results = -1; + maskInfo = -1; + return +end + +indices = []; + +maskInfo.size = [xd yd zd]; +maskInfo.analyzeFMT = 0; + +% Use the current directory. + +maskInfo.fPath = pwd; + +if size(otherVoxels,1) > 0 + indices = xd*yd*(otherVoxels(:,3)-1)+... + xd*(otherVoxels(:,2)-1)+... + otherVoxels(:,1); +end + +% Find the indices of all voxels +% to be included in analysis. + +maskInfo.iMask = find(ThreeDMask); + +% How many to remove from the end. + +maskInfo.remove = 0; +maskInfo.indices = indices; % index of other data. + +% Are the requested voxels already included, +% if not add to the list but mark for removal +% before actual SOM calculation. + +% Build a list of pointers to the data in the reduced set +% to where the voxels now live. +indexOfIndex = []; + +for ii = 1:size(indices) + if length(find(maskInfo.iMask == indices(ii))) == 0 + maskInfo.iMask = [maskInfo.iMask ;indices(ii)]; + maskInfo.remove = maskInfo.remove+1; + indexOfIndex = [indexOfIndex length(maskInfo.iMask)]; + end + indexOfIndex = [indexOfIndex find(maskInfo.iMask==indices(ii))]; +end + +maskInfo.indexOfIndex = indexOfIndex; + +% Now extract it all. + +fprintf('Extracting data...'); + +FourDData = reshape(FourDData,[prod([xd yd zd]) nTime]); + +results = FourDData(maskInfo.iMask,:); + +fprintf('\nDone\n'); + +clear FourDData +clear ThreeDMask + +return + +% +% All done. +% + diff --git a/som/SOM_MaskRead.m b/som/SOM_MaskRead.m new file mode 100644 index 00000000..b6158c17 --- /dev/null +++ b/som/SOM_MaskRead.m @@ -0,0 +1,25 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% A routine to read mask information +% +% function maskName = SOM_ReadMasks(maskName) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function maskName = SOM_MaskRead(maskName) + +maskName.ImgHDR = spm_vol(maskName.File); +maskName.ImgVol = spm_read_vols(spm_vol(maskName.File)); +maskName.ImgMask = maskName.ImgVol > maskName.ImgThreshold; +maskName.ROIIDX = find(maskName.ImgMask); + +return + +% +% all done. +% diff --git a/som/SOM_MatchBestComponent.m b/som/SOM_MatchBestComponent.m new file mode 100755 index 00000000..dea41e5f --- /dev/null +++ b/som/SOM_MatchBestComponent.m @@ -0,0 +1,58 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2010 +% +% Find the SOM component that best matches the template being +% passed. +% +% function results = SOM_MatchBestComponent(BinaryConnectivityMap,TheTemplate) +% +% BinaryConnectivityMap = the binarized connectivity map, +% 1 = connection +% 0 = none. +% +% TheTemplate = masking image with ROI. +% +% results = the count of the voxels in and out of the ROI, but containted to the brain. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_MatchBestComponent(BinaryConnectivityMap,TheTemplate) + +global SOMMem + +results = []; + +if isfield(SOMMem{1},'maskInfo') == 0 + return +end + +maskDim = size(TheTemplate); + +if sum(any(size(TheTemplate) - SOMMem{1}.maskInfo.size)) ~= 0 + fprintf('Masking template doesn''t match the size of the original data.\n'); + return +end + +BrainMask = zeros(SOMMem{1}.maskInfo.size); + +BrainMask(SOMMem{1}.maskInfo.iMask) = 1; + +NTheTemplate = TheTemplate .* BrainMask; + +ROIRemoved = sum(TheTemplate(:)-NTheTemplate(:)); + +NotTemplate = BrainMask.*(1 - NTheTemplate); + +InCount = sum(BinaryConnectivityMap(find(NTheTemplate))); +OutCount = sum(BinaryConnectivityMap(find(NotTemplate))); + +results = [InCount OutCount ROIRemoved]; + +return + +% +% All done. +% + diff --git a/som/SOM_MergeSOMS.m b/som/SOM_MergeSOMS.m new file mode 100755 index 00000000..fa7c9e75 --- /dev/null +++ b/som/SOM_MergeSOMS.m @@ -0,0 +1,59 @@ +% +% A function to merge SOM's +% +% function results = SOM_MergeSOMS(P,newName); +% +% P = array of file names to merge. +% +% P can also be the output of the matlab "dir" command. +% + +function results = SOM_MergeSOMS(P,newName) + +if exist('newName') == 0 + newName = 'combined_som'; +end + +if isstruct(P) + P1 = P(1).name; + nFiles = length(P); +else + P1 = P(1,:); + nFiles = size(P,1); +end + +[fPath fName fExt] = fileparts(P1); + +if strcmp(lower(fExt),'img') == 1 + analyzeFMT = 1; +else + analyzeFMT = 0; +end + +if analyzeFMT == 1 + printf('not working\n'); + return +end + +load(P1); + +s1 = zeros(size(somMap)); +for ifl = 1:nFiles + if isstruct(P) + load(P(ifl).name); + else + load(P(ifl,:)); + end + okidx = find(isfinite(somMap)); + tmpvol = 0*s1; + tmpvol(okidx) = somMap(okidx); + s1 = s1+tmpvol; +end + +somMap = s1; +save(newName,'somMap'); +results = somMap; + +% +% all done. +% \ No newline at end of file diff --git a/som/SOM_ModBasis.m b/som/SOM_ModBasis.m new file mode 100755 index 00000000..6a4b2b7e --- /dev/null +++ b/som/SOM_ModBasis.m @@ -0,0 +1,29 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% Ann Arbor MI. +% +% function results = SOM_ModBasis(pertVect,theSOM,indices,weights) +% +% pertVect = perturbing vector; +% nullSOM = a zeroed SOM (just need it for the size). +% SOMNeighborMap = array of indices of iSOM and distance. +% iteration = current iteration of the map. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_ModBasis(pertVect,results,SOMNeightborMap) +if length(pertVect) ~= size(results,1) + fprintf('Dimensions don''t match for SOM_ModBasis\n'); + return +end + +wts = reshape(SOMNeightborMap,[1 numel(SOMNeightborMap)]); +results = pertVect*wts; +return + +% +% All done. +% + diff --git a/som/SOM_NeighborDist.m b/som/SOM_NeighborDist.m new file mode 100755 index 00000000..9a64de0a --- /dev/null +++ b/som/SOM_NeighborDist.m @@ -0,0 +1,33 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% Calculate the distance between elements off the grid +% given the lattice spacing. +% +% function results = SOM_NeighborDist(nGrid) +% + +function results = SOM_NeighborDist(nGrid) + +distMat = zeros(nGrid,nGrid,nGrid*nGrid); + +xs = reshape((1:nGrid)'*ones(1,nGrid),[nGrid*nGrid 1]); +ys = reshape(ones(nGrid,1)*(1:nGrid),[nGrid*nGrid 1]); + +for iGrid = 1:nGrid*nGrid + distMat(:,:,iGrid) = reshape(sqrt((xs-xs(iGrid)).^2 + ... + (ys-ys(iGrid)).^2),... + [nGrid nGrid]); +end + +% Return the full 4-D matrix. + +results = reshape(distMat,[nGrid nGrid nGrid nGrid]);; + +return + +% +% All done +% diff --git a/som/SOM_NeighborMap.m b/som/SOM_NeighborMap.m new file mode 100755 index 00000000..263ee66d --- /dev/null +++ b/som/SOM_NeighborMap.m @@ -0,0 +1,25 @@ +% NEIGHBS = KOHNEIGHBS(GRIDSIZE,NEIGHBSIZE,LRATE) +% +% GRIDSIZE = [M,N] is the size of the grid. +% NEIGHBSIZE is a scale factor for the neighborhood function. +% LRATE is the learning rate. +% +% NEIGHBS is an M x N cell array whose elements are M x N matrices +% which define the degree of updating for each of the neighbors +% of that unit. + +function results = SOM_NeighborMap(GridSize,NeighSize) + +M = GridSize(1); +N = GridSize(2); + +mcoords = (1:M)' * ones(1,N); +ncoords = ones(M,1) * (1:N); +nvalues = exp(-(0:((M-1)^2+(N-1)^2))/(NeighSize^2)); + +for i = 1:M + for j = 1:N + distsqmat = (i-mcoords).^2 + (j-ncoords).^2 + 1; + results{i,j} = nvalues(distsqmat); + end +end diff --git a/som/SOM_ParseFileParam.m b/som/SOM_ParseFileParam.m new file mode 100755 index 00000000..9c58fb8b --- /dev/null +++ b/som/SOM_ParseFileParam.m @@ -0,0 +1,94 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Validate the time parameters +% pass for SOM_PreProcessData +% +% In general you should just set the ".File" field and let the code +% determine the rest of the fields. +% +% function type = SOM_ParseFileParam(type) +% +% type. +% File = full directory path and name to file. +% MaskFLAG = 0 no masking, 1 = masking. +% ImgThreshold = 0.75 (default) +% +% +% return 0 if no parameters passed +% -1 if File passed but doesn't exist. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function type = SOM_ParseFileParam(type); + +% Need access to standard masking value. + +global SOM + +% Did they pass the file name? + +type.OK = 1; + +if isfield(type,'File') == 0 + SOM_LOG('WARNING : No file information, skipping.'); + type.File = []; + type.MaskFLAG = 0; + type.ImgThreshold = SOM.defaults.MaskImgThreshold; + return +end + +if isempty(type.File) + SOM_LOG('WARNING : No file information, skipping.'); + type.MaskFLAG = 0; % Since the file name is empty, force the flag to be 0. + return +end + +% Is the file name valid? + +if exist(type.File,'file') == 0 + type.OK = -1; + SOM_LOG(sprintf('FATAL Error : Masking file %s specified doesn''t exist',type.File)); + return +end + +% Did they pass a flag? If they pass a valid name but did not pass the flag +% then assume that they wanted the flag to be on. + +if isfield(type,'MaskFLAG') == 0 + SOM_LOG('WARNING : Missing MaskFLAG, setting to 1.'); + type.MaskFLAG = 1; +end + +% Did they pass an image threshold? + +if isfield(type,'ImgThreshold') == 0 + SOM_LOG('WARNING : Missing ImgThreshold, setting to SOM.defaults.MaskImgThreshold'); + type.ImgThreshold = SOM.defaults.MaskImgThreshold; +end + +% Now check to see if numeric. +if isnumeric(type.MaskFLAG) == 0 + SOM_LOG('WARNING : MaskFLAG not numeric, setting to 1.'); + type.MaskFLAG = 1; +end + +% Force it to be 0 or 1. + +if type.MaskFLAG ~= 0 + type.MaskFLAG = 1; +end + +% If not numeric force it to be the default. + +if isnumeric(type.ImgThreshold) == 0 + SOM_LOG('WARNING : ImgThreshold not numeric, setting to SOM.defaults.MaskImgThreshold'); + type.ImgThreshold = SOM.defaults.MaskImgThreshold; +end + +return + diff --git a/som/SOM_Pearson.m b/som/SOM_Pearson.m new file mode 100755 index 00000000..26ee896f --- /dev/null +++ b/som/SOM_Pearson.m @@ -0,0 +1,72 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2007 +% +% +% A routine to calculate the Pearson Rho correlation coefficient +% between times series for voxels and SOM examplars. +% +% +% function Rho = SOM_Pearson(SelfOMap); +% +% global SOMMem +% +% SOMMem{slot}.theData(nVoxel,nTime); +% SelfOMap(nTime,nSOM); +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function Rho = SOM_Pearson(SelfOMap); + +global SOMMem + +% Force to use slot #1 + +slot = 1; + +% Check to see if the data is present as part of the global. + +if isfield(SOMMem{slot},'theData') == 0 + fprintf('Error - no data present in "global SOMMem"\n'); + Rho = []; + return +end + +nSpace = size(SOMMem{slot}.theData,1); +nTime1 = size(SOMMem{slot}.theData,2); + +nTime2 = size(SelfOMap,1); +nSOM = size(SelfOMap,2); + +if nTime1 ~= nTime2 + fprintf(['\n\nError, matrices not correct. Data(%d,%d)' ... + 'SelfOMap(%d,%d) are impossible to calc' ... + 'correlation.\n\n'],nSpace,nTime1,nTime2,nSOM); + Rho = []; + return +end + +% Calculate part in matlab since this is faster. + +mu_theData = mean(SOMMem{slot}.theData,2); +mu_SelfOMap = mean(SelfOMap,1); +sigma_theData = std(SOMMem{slot}.theData,0,2); +sigma_SelfOMap = std(SelfOMap,0,1); + +% Do the other part in mex code as that is faster. + +%Rho = SOM_PearsonEngin(SOMMem{slot}.theData,SelfOMap,... +% mu_theData,mu_SelfOMap,... +% sigma_theData,sigma_SelfOMap); + + +theData = SOMMem{slot}.theData - repmat(mu_theData,[1,nTime1]); +SelfOMap_mc = SelfOMap - repmat(mu_SelfOMap,[nTime2,1]); +Rho = theData*SelfOMap_mc./repmat(sigma_theData,[1,nSOM])./repmat(sigma_SelfOMap,[nSpace,1])/(nTime1-1); + +return + +% +% All done. +% diff --git a/som/SOM_PearsonEngin.c b/som/SOM_PearsonEngin.c new file mode 100755 index 00000000..1ae9fe22 --- /dev/null +++ b/som/SOM_PearsonEngin.c @@ -0,0 +1,280 @@ +/*----------------- +% +% Copyright Robert C. Welsh, Ann Arbor, MI, 2007 +% +% An auxillary routine to speed up the calculation of the +% correlation coefficient between two matrices. +% +% Rho = SOMPearsonEngin(theData,SelfOMap,mu_theData,mu_SelfOMap,Sigma_theData,sigma_SelfoMap) +% +% theData = theData(nSpace,nTime) +% SelfOMap = SelfOMap(nTime,nSOM) +% mu_theData = mean(theData,2) +% mu_SelfOMap = mean(SelfOMap,2) +% sigma_theData = std(theData,0,2) +% sigma_SelfOMap = std(SelfOMap,0,1); +% +% All this does is calculate: +% +% sum( +% +% To compile do: +% +% mex [-DSOMDEBUG] [-DSOMDEBUG2] SOM_CostFunction.c +% +% where -DSOMDEBUG[2] is a debug flag to the compiler. +% +% +% -Robert Welsh, 2006-12-12. +% +%------------------*/ + +#include +#include "mex.h" + +/*// Define a ^2 function to use in Euclidean Distance to avoid using math routine "pow", it's too slow!*/ +#define SQR(a) (a*a) + +#define EPS 1e-10 + +/* + Results area - make it global so we can access it for multiple calls. +*/ + +static mxArray *resultsMX=NULL; +static double *timeSeries=NULL; + +/* + Exit routine - need to use "clear SOM_PearsonEngin" to free up the memory. +*/ + +static void SOM_ExitPearson(void) +{ + + mexPrintf("SOM_ExitPearson has been called.\n"); + if (resultsMX != NULL) + { + mexPrintf("Destroying persistent\n"); + mxDestroyArray(resultsMX); + } + if (timeSeries != NULL) + { + mexPrintf("Removing 'timeSeries'\n"); + mxFree(timeSeries); + timeSeries = NULL; + } +} + +void SOM_PearsonEnginUsage() +{ + mexPrintf("\nUsage : \n\n"); + mexPrintf(" Rho = SOMPearsonEngin(theData,SelfOMap,mu_theData,mu_SelfOMap,Sigma_theData,sigma_SelfoMap)\n"); + mexPrintf("\n"); + mexPrintf(" theData = theData(nSpace,nTime)\n"); + mexPrintf(" SelfOMap = SelfOMap(nTime,nSOM)\n"); + mexPrintf(" mu_theData = mean(theData,2)\n"); + mexPrintf(" mu_SelfOMap = mean(SelfOMap,1)\n"); + mexPrintf(" sigma_theData = std(theData,0,2)\n"); + mexPrintf(" sigma_SelfOMap = std(SelfOMap,0,1)\n\n"); +} + +/* + This is the main function that is called by MATLAB + +*/ + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + mxArray *theDataMX; + mxArray *SelfOMapMX; + + mxArray *mu_theDataMX; + mxArray *mu_SelfOMapMX; + + mxArray *sigma_theDataMX; + mxArray *sigma_SelfOMapMX; + + double *results; + + double *theData; + double *SelfOMap; + + double *mu_theData; + double *mu_SelfOMap; + + double *sigma_theData; + double *sigma_SelfOMap; + + + int nTime; + int nTimeSOM; + + int nSOM; + int nVoxels; + + int iSOM; + int iTime; + int iVoxel; + + double tmp1; + + int idx1; + int idx2; + + /*// Now the function.*/ + + /* Register the exit function */ + + mexAtExit(SOM_ExitPearson); + + /*// Did they pass in enough arguements?*/ + + if (nrhs != 6) + { + SOM_PearsonEnginUsage(); + mexErrMsgTxt("Error, wrong number of input parameters."); + } + + /*// Get the pointers to the data and self-organizing map.*/ + + theDataMX = prhs[0]; + SelfOMapMX = prhs[1]; + mu_theDataMX = prhs[2]; + mu_SelfOMapMX = prhs[3]; + sigma_theDataMX = prhs[4]; + sigma_SelfOMapMX = prhs[5]; + + /*// Now get the dimensions of each.*/ + + nVoxels = mxGetM(theDataMX); + nTime = mxGetN(theDataMX); + + nTimeSOM = mxGetM(SelfOMapMX); + nSOM = mxGetN(SelfOMapMX); + + theData = mxGetPr(theDataMX); + SelfOMap = mxGetPr(SelfOMapMX); + + mu_theData = mxGetPr(mu_theDataMX); + mu_SelfOMap = mxGetPr(mu_SelfOMapMX); + + sigma_theData = mxGetPr(sigma_theDataMX); + sigma_SelfOMap = mxGetPr(sigma_SelfOMapMX); + + /*// Make sure the dimensions are good.*/ + if (nTime != nTimeSOM) + { + SOM_PearsonEnginUsage(); + mexPrintf("Error, time points in 'SelfOMap' and time points of 'theData' don't match!\n"); + mexErrMsgTxt("Aborting."); + }; + + if (nVoxels != mxGetM(mu_theDataMX) || mxGetN(mu_theDataMX) != 1) + { + SOM_PearsonEnginUsage(); + mexPrintf("Did you pass in mean(theData,2) ?\n"); + mexErrMsgTxt("Aborting."); + }; + + if (nVoxels != mxGetM(sigma_theDataMX) || mxGetN(sigma_theDataMX) != 1) + { + SOM_PearsonEnginUsage(); + mexPrintf("Did you pass in std(theData,0,2) ?\n"); + mexErrMsgTxt("Aborting."); + }; + + if (nSOM != mxGetN(mu_SelfOMapMX) || mxGetM(mu_SelfOMapMX) != 1) + { + SOM_PearsonEnginUsage(); + mexPrintf("Did you pass in mean(SelfOMap,1) ?\n"); + mexErrMsgTxt("Aborting."); + }; + + if (nSOM != mxGetN(sigma_SelfOMapMX) || mxGetM(sigma_SelfOMapMX) != 1) + { + SOM_PearsonEnginUsage(); + mexPrintf("Did you pass in std(SelfOMap,0,1) ?\n"); + mexErrMsgTxt("Aborting."); + }; + +#if SOMDEBUG + mexPrintf("theData(%d,%d), SelfOMap(%d,%d)\n",nVoxels,nTime,nTimeSOM,nSOM); + mexPrintf("number of returns : %d\n",nlhs); +#endif + + /*// Results returned in a new matrix.*/ + + if (resultsMX == NULL) + { + mexPrintf("resultsMX is unknown\n"); + resultsMX = mxCreateDoubleMatrix(nVoxels,nSOM,mxREAL); + mexMakeArrayPersistent(resultsMX); + timeSeries = mxMalloc(sizeof(double)*nTime); + mexMakeMemoryPersistent(timeSeries); + } + else + { + mexPrintf("resultsMX is known, %d\n",resultsMX); + /* Check it's size */ + if (mxGetM(resultsMX) != nVoxels || mxGetN(resultsMX) != nSOM) + { + mexPrintf("Must destroy persistent array and recreate.\n"); + mxDestroyArray(resultsMX); + resultsMX = mxCreateDoubleMatrix(nVoxels,nSOM,mxREAL); + mexMakeArrayPersistent(resultsMX); + mxFree(timeSeries); + timeSeries = mxMalloc(sizeof(double)*nTime); + mexMakeMemoryPersistent(timeSeries); + mexPrintf("Recreated 'timeSeries' array\n"); + } + } + + /* + plhs[0] = mxCreateDoubleMatrix(nVoxels,nSOM,mxREAL); + */ + + plhs[0] = resultsMX; + +#if SOMDEBUG + mexPrintf("Created output array of size %d x %d\n",mxGetM(plhs[0]),mxGetN(plhs[0])); +#endif + + /*// get the pointer to the data area of the output array.*/ + + results = mxGetPr(plhs[0]); + + /*// Now run the calculation.*/ + + for (iVoxel = 0; iVoxel < nVoxels; iVoxel++) + { + /* Pull the time series for this voxel since we keep using it */ + for ( iTime = 0; iTime < nTime; iTime++) + timeSeries[iTime] = theData[iVoxel+iTime*nVoxels]; + /* */ + for (iSOM = 0; iSOM < nSOM; iSOM++) + { + idx1 = iSOM*nTime; + idx2 = nVoxels*iSOM; + tmp1 = 0; + for ( iTime = 0; iTime < nTime; iTime++) + { +#if SOMDEBUG2 + mexPrintf("|%f - %f|^2\n",theData[iVoxel+iTime*nVoxels],SelfOMap[idx1+iTime]); +#endif + tmp1 += ( (timeSeries[iTime]-mu_theData[iVoxel])* + (SelfOMap[idx1+iTime]-mu_SelfOMap[iSOM]) ); + /* tmp1 += ( (theData[iVoxel+iTime*nVoxels]-mu_theData[iVoxel])* + (SelfOMap[idx1+iTime]-mu_SelfOMap[iSOM]) );*/ + } + results[idx2+iVoxel] = tmp1/sigma_theData[iVoxel]/sigma_SelfOMap[iSOM]/(nTime-1); +#if SOMDEBUG2 + mexPrintf("\n"); +#endif + } + } +} + + +/*// All done.*/ + diff --git a/som/SOM_PowerSpect.m b/som/SOM_PowerSpect.m new file mode 100755 index 00000000..8baa45f0 --- /dev/null +++ b/som/SOM_PowerSpect.m @@ -0,0 +1,61 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2010 +% +% +% A routine to return power spectrum of data +% +% theData = theData(space,time) (this is the +% standard format being used in this SOM +% implementation). +% +% sample = sample period (TR in fmri language) +% +% function [results, powerParams] = SOM_PowerSpect(theData,sample) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results, powerParams] = SOM_PowerSpect(theData,sample) + +% Save input for posterity. + +powerParams.sample = sample; + +%Determine the Nyquist criterion + +nyquist = 1/sample/2; + +powerParams.nyquist = nyquist; + +% How big is our sample. + +if size(theData,2) == 1 + if size(theData,1) > 1 + theData = theData'; + fprintf('Transposing data for you, assuming the vector is time data.\n'); + end +end + +N = size(theData,2); + +% Make a frequency baseline. + +deltaF = nyquist/(floor(N/2)-1); + +freq = (-floor(N/2):floor(N/2)-1)*deltaF; + +powerParams.deltaF = deltaF; +powerParams.freq = freq; + +% Get the fft of the data. + +ffttheData = fftshift(fft(theData,[],2),2); + +powerParams.fft = ffttheData; + +results = ffttheData.*conj(ffttheData)/2/pi; + +% +% return +% diff --git a/som/SOM_PreProcessData.m b/som/SOM_PreProcessData.m new file mode 100644 index 00000000..1c0d0ca1 --- /dev/null +++ b/som/SOM_PreProcessData.m @@ -0,0 +1,569 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Pre-processing data for connectivity analysis. +% +% [D0 parameters] = SOM_PreProcessData(parameters) +% +% +% Input Parameters that we need for preparing the data +% +% masks. +% +% Grey Matter mask and flag to use it. +% +% (the grey matter mask is intersected with the epi mask) +% +% grey. +% File = full directory path and name to file. +% [ImgThreshold = 0.75 (default) ] +% +% +% White Matter mask +% +% white. +% File = full directory path and name to file. +% [ImgThreshold = 0.75 (default) ] +% +% +% csf mask +% +% csf. +% File = full directory path and name to file. +% [ImgThreshold = 0.75 (default) ] +% +% +% +% Brain matter mask +% +% epi. +% File = full directory path and name to file. +% [ImgThreshold = 0.75 (default) ] +% +% +% data. +% +% run[iRun]. +% +% P = full directory path to time-series data. +% +% MotionParameters = array of motion parameters +% +% nTIME = number of time points to test. +% +% MaskFLAG = 0 don't do any masking and grab all of the data +% = 1 mask using either what is in parameters.epi +% or by building a subject specific mask with +% SOM_CreateMask +% +% +% RegressFLAGS. +% +% prinComp = 0 use average if available +% # use [N] principle components specified +% +% global = 0 no global regression +% 1 do global regression +% +% csf = 0 no CSF regression +% 1 CSF regression if 'csf' is filled +% above. +% +% white = 0 no white matter regresson +% 1 white matter regression if 'white' is filled +% above. +% +% motion = 0 no motion regression +% 1 motion regression (default if MotionParameters +% are present) +% +% order = the order to perform the regressions etc +% D = detrend +% G = global +% W = white matter +% C = csf +% M = motion +% B = bandpass +% +% Suggested order is "D[G]CWMB", if omitted +% then this is the order assumed. Flags still +% have to be set to yes though. +% +% TIME. +% +% run[iRun]. +% +% TR = repetition time +% +% BandFLAG = 0 no band pass filter +% 1 apply bandpass filter +% +% TrendFLAG < 0 no linear detrending +% # use [N]-order polynomial to detrend. +% +% LowF = low frequency band cut +% +% HiF = high frequency band cut +% +% gentle = 0, no rolling +% 1, rolling +% +% padding = # time points to pad on left/right +% +% whichFilter = 0, use the MATLAB filter +% #, use SOM_Filter_FFT +% +% fraction = fraction of variance for principle components +% analysis. Default 1. +% +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +% Modified Nov 8, 2011 to have nTIME be part of data.run +% structure, previously it was part of the TIME.run structure. + +% 2012.01.12 Modified to allow editing of time-series data +% 2012.01.12 after conversation with Mike Milham on removing movement +% 2012.01.12 outliers. RCWelsh + +% 2011.11.18 - RCWelsh : Fixed nSPACE -> nSPACE(1) + +function [D0 parameters] = SOM_PreProcessData(parameters) + +global SOM + +% Save the parameters pass for debugging later + +save parameters_debug parameters + +% Set up the defaults + +SOM_SetDefaults; + +% Default return is to fail. + +D0 = -1; + +% + +parameters.startCPU.preprocess = cputime; + +% Did they pass the input data? + +parameters.data = SOM_CheckDataStructure(parameters); + +if parameters.data.OK ~= 1 + SOM_LOG('FATAL ERROR : Missing information in parameter.data'); + return +end + +% Check the time parameters. + +parameters.TIME = SOM_CheckTimeParams(parameters); + +if parameters.TIME.OK ~= 1 + SOM_LOG('FATAL ERROR : Missing TIME parameters'); + return +end + +% Files needed for masking? + +parameters.masks = SOM_CheckMasks(parameters); + +if parameters.masks.OK ~= 1 + SOM_LOG('FATAL ERROR : Something wrong with masks definitions.') + return +end + +% Now prepare based on parameters. + +% Store the head and names for some quick checking. + +fileINDEXTemp = 0; +filesToCheck = []; + +if parameters.masks.grey.MaskFLAG == 0 + parameters.masks.grey.ImgMask = 1; + parameters.masks.grey.ROIIDX = []; % Superflous but consistent. +else + parameters.masks.grey = SOM_MaskRead(parameters.masks.grey); + % store temp + fileINDEXTemp = fileINDEXTemp + 1; + filesToCheck(fileINDEXTemp).hdr = parameters.masks.grey.ImgHDR; +end + +if parameters.masks.white.MaskFLAG == 0 + parameters.masks.white.ImgMask = 0; + parameters.masks.white.ROIIDX = []; +else + parameters.masks.white = SOM_MaskRead(parameters.masks.white); + % store temp + fileINDEXTemp = fileINDEXTemp + 1; + filesToCheck(fileINDEXTemp).hdr = parameters.masks.white.ImgHDR; +end + +if parameters.masks.csf.MaskFLAG == 0 + parameters.masks.csf.ImgMask = 0; + parameters.masks.csf.ROIIDX = []; +else + parameters.masks.csf = SOM_MaskRead(parameters.masks.csf); + + % store temp + fileINDEXTemp = fileINDEXTemp + 1; + filesToCheck(fileINDEXTemp).hdr = parameters.masks.csf.ImgHDR; +end + +% Check the reression flags. + +parameters.RegressFLAGS = SOM_CheckRegressFLAGS(parameters); + +if parameters.RegressFLAGS.OK < 1 + SOM_LOG('FATAL : SOM_CheckRegressFLAGS returned an error'); + return +end + + +curDir = pwd; + +% Where is the data? + +[fP fN fE] = fileparts(parameters.data.run(1).P(1,:)); + +% Make the mask file, masking out non-brain. Using a standard mask. + +if parameters.data.MaskFLAG == 1 + if parameters.masks.epi.MaskFLAG == 0 + SOM_LOG('Calculating subject specific epi mask'); + % Create the mask from the very first/only run. + parameters.maskHdr = SOM_CreateMask(parameters.data.run(1).P); + else + parameters.maskHdr = spm_vol(parameters.masks.epi.File); + end + % store temp + fileINDEXTemp = fileINDEXTemp + 1; + filesToCheck(fileINDEXTemp).hdr = parameters.maskHdr; +else + % changed this on 2012-03-29 (RCWelsh), this will force some sort of masking, but that is okay. + % This will prevent SOM_CalculateCorrelationImages from failing if no mask is indicated at all + parameters.maskHdr = SOM_CreateMask(parameters.data.run(1).P); + %parameters.maskHdr.fname = []; % If no name then SOM_PrepData can deal. +end + +% +% Now make sure all headers comply with each other: +% + +for iHDR = 1:fileINDEXTemp + if SOM_SpaceVerify(parameters.data.run(1).hdr,filesToCheck(iHDR).hdr) ~= 1 + SOM_LOG('FATAL ERROR : Error with consistent (mask) image space definition.'); + return + end +end + +% Read in the data. + +% Loop on the runs to be able to read it all in. + +% We need to be able to figure out how many time points total. +% and the space points by definition have to be the same! + +nTIME = []; +nSPACE = []; + +D0RUN = []; + +for iRUN = 1:length(parameters.data.run) + + [D0RUN(iRUN).D0 parameters.maskInfo] = SOM_PrepData(parameters.data.run(iRUN).P,parameters.maskHdr.fname,[]); + + % Trim the data as needed. + + if size(D0RUN(iRUN).D0,2) > parameters.data.run(iRUN).nTIME; + D0RUN(iRUN).D0 = D0RUN(iRUN).D0(:,1:parameters.data.run(iRUN).nTIME); + SOM_LOG(sprintf('WARNING : Trimming data to adhere to length specified in parameters.data.run.nTIME : %d',parameters.data.run(iRUN).nTIME)); + end + + % Capture how many time points we have read. + + parameters.data.run(iRUN).nTimeAnalyzed = size(D0RUN(iRUN).D0,2); + + % Record for all runs. + + nTIME = [nTIME parameters.data.run(iRUN).nTimeAnalyzed]; + nSPACE = [nSPACE size(D0RUN(iRUN).D0,1)]; + + % Loop on the preprocessing steps requested. + + for iOrder = 1:length(parameters.RegressFLAGS.order) + + % Determine which is the present step + % + % possibilities are : DGCWMB + % + switch parameters.RegressFLAGS.order(iOrder) + + % + % Detrend + % + case 'D' + + % Detrend the data. + % + % SPM wants the data presented as Time X Space, hence the transpose + % operator. This will also mean center the data. + % + + SOM_LOG('STATUS : Doing detrending.'); + parameters.startCPU.run(iRUN).detrend = cputime; + + if parameters.TIME.run(iRUN).TrendFLAG > 0 + D0RUN(iRUN).D0 = spm_detrend(D0RUN(iRUN).D0',parameters.TIME.run(iRUN).TrendFLAG)'; + end + + parameters.stopCPU.run(iRUN).detrend = cputime; + + % + % Global (controversial, prepare to defend your usage) + % + case 'G' + + parameters.startCPU.run(iRUN).global = cputime; + + % Global regression + + parameters.TIME.run(iRUN).GS = SOM_GlobalCalc(D0RUN(iRUN).D0); + + SOM_LOG('STATUS : Doing global regression'); + D0RUN(iRUN).D0 = SOM_RemoveConfound(D0RUN(iRUN).D0,parameters.TIME.run(iRUN).GS); + + parameters.stopCPU.run(iRUN).global = cputime; + + % + % CSF, helps pick up residual physio, or so says the theory? + % + case 'C' + % Remove the CSF. + + parameters.startCPU.run(iRUN).csf = cputime; + + if parameters.masks.csf.MaskFLAG > 0 + + SOM_LOG('STATUS : CSF Regression'); + parameters.masks.csf.IDX = []; + + % Now convert the ROI indices to the indices in the mask. + + parameters.masks.csf.IDX = SOM_ROIIDXnMASK(parameters,parameters.masks.csf.ROIIDX); + + if length(parameters.masks.csf.IDX) < 1 + SOM_LOG(sprintf('STATUS : Not enough voxels to determine CSF time course')) + else + SOM_LOG(sprintf('STATUS : %d CSF Voxels in extracted data.',length(parameters.masks.csf.IDX))); + parameters.masks.csf.run(iRUN).PRINCOMP = []; + % + % Are we doing principle components are we taking the mean of the ROI? + % + if parameters.RegressFLAGS.prinComp > 0 + parameters.masks.csf.run(iRUN).PRINCOMP = SOM_PrinComp(D0RUN(iRUN).D0(parameters.masks.csf.IDX,:),parameters.TIME.run(iRUN).fraction); + % How many components are we to use? + parameters.masks.csf.run(iRUN).nComp = min([parameters.RegressFLAGS.prinComp size(parameters.masks.csf.run(iRUN).PRINCOMP.PCScore,2)]); + parameters.masks.csf.run(iRUN).regressors = (parameters.masks.csf.run(iRUN).PRINCOMP.PCScore(:,1:parameters.masks.csf.run(iRUN).nComp)); + else + parameters.masks.csf.run(iRUN).regressors = mean(D0RUN(iRUN).D0(parameters.masks.csf.IDX,:))'; + end + + % Now remove them. + + D0RUN(iRUN).D0 = SOM_RemoveMotion(D0RUN(iRUN).D0,parameters.masks.csf.run(iRUN).regressors); + end + else + parameters.masks.csf.run(iRUN).regressors = []; + SOM_LOG('WARNING : * * * * * * * * * * * *'); + SOM_LOG('WARNING : CSF regression speficied but no CSF regression mask available.'); + SOM_LOG('WARNING : * * * * * * * * * * * *'); + end + + parameters.stopCPU.run(iRUN).csf = cputime; + + % + % White matter, helps pick up residual physio, or so says the theory? + % + case 'W' + % Now remove the White Matter. + + parameters.startCPU.run(iRUN).white = cputime; + + if parameters.masks.white.MaskFLAG > 0 + + SOM_LOG('STATUS : WM Regression'); + parameters.masks.white.IDX = []; + + % Now convert the ROI indices to the indices in the mask. + + parameters.masks.white.IDX = SOM_ROIIDXnMASK(parameters,parameters.masks.white.ROIIDX); + + SOM_LOG(sprintf('STATUS : %d WM Voxels in extracted data.',length(parameters.masks.white.IDX))); + + % Are we regressing out the principle components or the mean. + + parameters.masks.white.run(iRUN).PRINCOMP = []; + + % + % Are we doing principle components are we taking the mean of the ROI? + % + if parameters.RegressFLAGS.prinComp > 0 + parameters.masks.white.run(iRUN).PRINCOMP = SOM_PrinComp(D0RUN(iRUN).D0(parameters.masks.white.IDX,:),parameters.TIME.run(iRUN).fraction); + % How many components are we to use? + parameters.masks.white.run(iRUN).nComp = min([parameters.RegressFLAGS.prinComp size(parameters.masks.white.run(iRUN).PRINCOMP.PCScore,2)]); + parameters.masks.white.run(iRUN).regressors = (parameters.masks.white.run(iRUN).PRINCOMP.PCScore(:,1:parameters.masks.white.run(iRUN).nComp)); + else + parameters.masks.white.run(iRUN).regressors = mean(D0RUN(iRUN).D0(parameters.masks.white.IDX,:))'; + end + + % Now remove them. + + D0RUN(iRUN).D0 = SOM_RemoveMotion(D0RUN(iRUN).D0,parameters.masks.white.run(iRUN).regressors); + else + parameters.masks.white.run(iRUN).regressors = []; + SOM_LOG('WARNING : * * * * * * * * * * * *'); + SOM_LOG('WARNING : WM reression speficied but no WM regression mask available.'); + SOM_LOG('WARNING : * * * * * * * * * * * *'); + end + + parameters.stopCPU.run(iRUN).white = cputime; + + % + % Motion, just because we can and typically I like to regress out + % also the 1st motion derivative. + % + case 'M' + % Regress out the motion etc. + + parameters.startCPU.run(iRUN).motion = cputime; + + if parameters.RegressFLAGS.motion > 0 + D0RUN(iRUN).D0 = SOM_RemoveMotion(D0RUN(iRUN).D0,parameters.data.run(iRUN).MotionParameters(1:parameters.data.run(iRUN).nTimeAnalyzed,:)); + SOM_LOG('STATUS : Motion Correction Implemented'); + else + SOM_LOG('WARNING : * * * * * * * * * * * *'); + SOM_LOG('WARNING : Motion regression speficied, but motion regression internally turned off???'); + SOM_LOG('WARNING : * * * * * * * * * * * *'); + end + + parameters.stopCPU.run(iRUN).motion = cputime; + + % + % Bandpass filter, because low-freqency BOLD should be band-passed. + % + case 'B' + + parameters.startCPU.run(iRUN).band = cputime; + + % Now band-pass filter + + if parameters.TIME.run(iRUN).BandFLAG > 0 + [D0RUN(iRUN).D0 b] = SOM_Filter(D0RUN(iRUN).D0,... + parameters.TIME.run(iRUN).TR,... + parameters.TIME.run(iRUN).LowF,... + parameters.TIME.run(iRUN).HiF,... + parameters.TIME.run(iRUN).gentle,... + parameters.TIME.run(iRUN).padding,... + parameters.TIME.run(iRUN).whichFilter); + parameters.TIME.run(iRUN).b = b(1,:); + SOM_LOG('STATUS : Band Pass Filter Implemented.'); + else + parameters.TIME.run(iRUN).b = []; + SOM_LOG(sprintf('WARNING : No Band Pass Filter Speficied for this run : %d.',iRUN)); + end + + parameters.stopCPU.run(iRUN).band = cputime; + + % + % Major error. + % + otherwise + D0RUN(iRUN).D0 = -1; + D0 = -1; + SOM_LOG(sprintf('FATAL : regression step not recongnized : %s',parameters.RegressFLAGS.order)); + return + end + end +end + +% Make sure the space is all the same! + +if length(nSPACE>1) + if any(diff(nSPACE)) + SOM_LOG(sprintf('FATAL : Resulting number of voxels in each run is inconsistent')); + for iRUN=1:length(nSPACE) + SOM_LOG(sprintf('FATAL : Run %d has %d voxels',iRUN,nSPACE(iRUN))); + end + return + end +end + +% Number of time points before editing + +cnTIME = [0 cumsum(nTIME)]; + +SOM_LOG(sprintf('STATUS : Starting with data : %d space by %d time-points',nSPACE(1),cnTIME(end))); + +% 2011.11.18 - RCWelsh : Fixed nSPACE -> nSPACE(1) + +% Edit the data if needed. + +enTIME = []; + +for iRUN = length(parameters.data.run) + if isfield(parameters.data.run(iRUN),'censorVector') + D0RUN(iRUN).D0 = SOM_editTimeSeries(D0RUN(iRUN.D0),parameters.data.run(iRUN).censorVector); + if D0RUN(iRUN).D0 == -1 + SOM_LOG('FATAL : SOM_editTimeSeries failed.'); + exit + else + enTIME = [enTIME size(D0RUN(iRUN).D0,2)]; + SOM_LOG(sprintf('STATUS : Changed run %d from %d time-points to %d',iRUN,nTIME(iRUN),enTIME(iRUN))); + end + end +end + +% Now calculate the new length, that is if we need to. + +if length(enTIME) > 0 + SOM_LOG(sprintf('STATUS : Edited data to : %d space by total %d time-points',nSPACE(1),cenTIME(end))); +else + enTIME=nTIME; + SOM_LOG(sprintf('STATUS : No editing of data : %d space by total % time-points',nSPACE(1),cnTIME(end))); +end + +cenTIME = [0 cumsum(enTIME)]; + +% We can store all of this for posterity + +parameters.data.nTIME = nTIME; +parameters.data.enTIME = enTIME; + +% Now contactenate the data. + +D0 = zeros(nSPACE(1),cenTIME(end)); + +for iRUN = 1:length(parameters.data.run) + D0(:,cenTIME(iRUN)+1:cenTIME(iRUN+1)) = D0RUN(iRUN).D0; +end + +parameters.stopCPU.preprocess = cputime; + +SOM_LOG(sprintf('STATUS : Total cpu usage during pre-processing step : %f sec',parameters.stopCPU.preprocess - parameters.startCPU.preprocess)); + +% +% All done. +% + +return + + diff --git a/som/SOM_PrepData.m b/som/SOM_PrepData.m new file mode 100755 index 00000000..fe5684fd --- /dev/null +++ b/som/SOM_PrepData.m @@ -0,0 +1,250 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005-2011 +% +% Routine to read time-series data. +% +% function [results, maskInfo, analyzeFMT] = SOM_PrepData(P,PMask,otherVoxels) +% +% P is array of file names (like that returned from spm_get) +% PMask is a file name for a binary mask. +% If PMask if empty then there is no mask and all of the data is returned. +% otherVoxels is an array of explicitly desired voxels. +% +% You have two options for input +% +% If the PMask has the ending of ".img" then use analyze, if ".nii" +% or ".nii.gz" then it's NIFTI, else assume .mat files. +% +% 1) Use of analyze img/hdr pairs, .nii, or .nii.gz such as in SPM2/SPM5/SPM8 +% +% P is an array of img file names +% PMask is a name of binary mask volume +% +% 2) Use of matlab ".mat" files. +% +% If you use this option then the mask file "PMask" +% must contain a variable called "som_mask", which +% has the dimensionality of your data, but is a binary +% image (1=use, 0=don't use). +% +% The time-series data should only have a single variable +% contained in the time-point ".mat" file. The reading +% code will use whatever variable is available, regardless +% of name. +% +% Only those voxels that are included in the mask are read. +% +% "otherVoxels" +% +% This is a nVoxels x 3 array of indices. The indices must lie on +% the axis of your image. A scalar index is calculated from the +% indices into your image. +% +% You would use this array to guarantee inclusion of voxels not +% present in the mask. +% +% +% NOTE : Presently the code can only read a series of 3D files. +% There is NO support for 4D files yet. +% +% However, you can just read your own data and reshape +% appropriately for calling SOM_CalculateMap. +% +% You should look at "SOM_MaskData" for 4D. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results, maskInfo, analyzeFMT] = SOM_PrepData(P,PMask,otherVoxels) + +% We will store the mask information here for +% cruising the elements. This is a kludge at the moment. + +global SOMMem + +slot = 1; + +SOM_LOG('STATUS : Reading data'); + +results = []; +maskInfo = []; + +% If there is no mask then we will use the first image of the data. + +if isempty(PMask) == 1 + [mfPath mfName mfExt] = fileparts(P(1,:)); +else + [mfPath mfName mfExt] = fileparts(PMask); +end + +[fPath fName fExt] = fileparts(P(1,:)); + +% Modify to read in either 3d images in img or 4d in nii + +switch (lower(fExt)) + case '.img' + analyzeFMT = 1; + case '.nii' + analyzeFMT = 2; + case '.nii.gz' + analyzeFMT = 3; + otherwise + analyzeFMT = 0; +end + +% If we are NOT using mat files. + +PHDR = spm_vol(P); + +if analyzeFMT > 0 + if isempty(PMask) == 1 + maskInfo.header = PHDR(1); + else + maskInfo.header = spm_vol(PMask); + end + maskInfo.analyzeFMT = 1; + som_mask = spm_read_vols(maskInfo.header); + % Use everything if no mask is specified. + som_mask = som_mask>0; +else + maskInfo.analyzeFMT = 0; + maskInfo.fPath = fPath; + load(PMask); + % + % hopefully this results in the loading of a variable called + % "som_mask" + % + if exist('som_mask') ~= 1 + fprintf('\nError in loading the ''som_mask.mat'', the variable'); + fprintf('som_mask is missing.\n'); + results = []; + return + end +end + +% Did they pass any requests for +% specific voxels to be extracted? + +if exist('otherVoxels') ~= 1 + otherVoxels = []; +end + +[xd yd zd] = size(som_mask); +indices = []; + +maskInfo.size = [xd yd zd]; + +if size(otherVoxels,1) > 0 + indices = xd*yd*(otherVoxels(:,3)-1)+... + xd*(otherVoxels(:,2)-1)+... + otherVoxels(:,1); +end + +% Find the indices of all voxels +% to be included in analysis. + + +%if isempty(PMask) == 1 +% som_mask = ones(size(som_mask)); +%end + +maskInfo.iMask = find(som_mask); + +% How many to remove from the end. + +maskInfo.remove = 0; +maskInfo.indices = indices; % index of other data. + +% Are the requested voxels already included, +% if not add to the list but mark for removal +% before actual SOM calculation. + +% Build a list of pointers to the data in the reduced set +% to where the voxels now live. + +indexOfIndex = []; + +for ii = 1:size(indices) + if length(find(maskInfo.iMask == indices(ii))) == 0 + maskInfo.iMask = [maskInfo.iMask ;indices(ii)]; + maskInfo.remove = maskInfo.remove+1; + indexOfIndex = [indexOfIndex length(maskInfo.iMask)]; + end + indexOfIndex = [indexOfIndex find(maskInfo.iMask==indices(ii))]; +end + +maskInfo.indexOfIndex = indexOfIndex; + +% Initialize matrix for time-series data. + +results = zeros(length(maskInfo.iMask),length(PHDR)); + +% Now extract it all. + +% If we have Luis Hernandez's "read_nii_img, read_nii_hdr, +% img_endian", then we will read from that as it's a lot faster. + +FASTCODEPRESENT=1; + +FASTCODE={'nifti','SOM_read_nii_img','SOM_read_nii_hdr','SOM_img_endian'}; + +for iCODE = 1:length(FASTCODE) + if exist(FASTCODE{iCODE}) ~= 2 + FASTCODEPRESENT = 0 + end +end + +if FASTCODEPRESENT & analyzeFMT == 2 + tic; + %theVols = SOM_read_nii_img(P); % Depreceated on 2012-03-23 - RCWelsh and replaced with SOM_ReadNII which uses 'nifti' + theVols = SOM_ReadNII(P); + results = (theVols(:,maskInfo.iMask))'; + toctime = toc; + SOM_LOG(sprintf('STATUS : Fast read code implemented, %s',toctime)); +else + fprintf('Reading Data\n\n'); + for iP = 1:length(PHDR) + fprintf('\b\b\b%03d',iP); + if analyzeFMT > 0 + % + % Analyze file + % + theVol = spm_read_vols(PHDR(iP,:)); + else + % + % Using a ".mat" file, pick the first variable found. + % Be careful, could result in big error! + % Better to use img/hdr pairs for now. + % + tmpVol = load(P(iP,:)); + fldNM = fieldnames(tmpVol); + theVol = getfield(tmpVol,fldNM{1}); + end + % + % Now do sanity check of the volume size. + % + if any(maskInfo.size - size(theVol)) + SOM_LOG('FATAL ERROR : reading time-series data, size doesn''t match'); + SOM_LOG(sprintf('FATAL ERROR : Size of mask %d %d %d, size of current volume #%d : %d %d %d',maskInfo.size,iP,size(theVol))); + results = []; + return + end + results(:,iP) = theVol(maskInfo.iMask); + clear theVol; + end + fprintf('\nDone\n'); +end + + +SOM_LOG('STATUS : Prepdata Done'); + +clear theVol; + +SOMMem{slot}.maskInfo = maskInfo; + +return + +% +% All done. +% diff --git a/som/SOM_PrinComp.m b/som/SOM_PrinComp.m new file mode 100755 index 00000000..7a5502c8 --- /dev/null +++ b/som/SOM_PrinComp.m @@ -0,0 +1,78 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Calculate the Principle components from some confound time-series +% data +% +% function results = SOM_PrincipleComponents(theData,dataFraction) +% +% theData = space x time +% +% you should linear detrend the data first. +% +% e.g. theData = spm_detrend(theData',1)'; +% +% The default is to take all voxels equally, howver, you can +% also specify that top X% of those with variance should be used +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +% +% 2011.11.18 - RCWelsh - Fixed error on subscript to line below to +% reflect change: +% size(PCScore,1) -> size(PCScore,2); +% + +function results = SOM_PrinComp(theData,dataFraction) + +global SOM + +results.startCPU = cputime; + +if isfield(SOM,'NumberPrincipleComponents') == 0 + SOM.NumberPrincipleComponents = 50; +end + +if exist('dataFraction') == 0 + dataFraction = 1.0; +else + dataFraction = min([1.0 max([dataFraction .01])]); +end + +TVAR = var(theData,[],2); + +TOTALVAR = sum(var(theData,[],2)); + +VARIDX = sortrows([TVAR [1:length(TVAR)]'],-1); + +NIDX = max([2 round(dataFraction*length(TVAR))]); + +VOXIDX = VARIDX(1:NIDX,2); + +[PCCoeff PCScore PCLatent PCT2] = princomp(theData(VOXIDX,:)'); + +% +% Calculate the percent variance explained in the data. +% + +VARCOMP = []; + +SOM_LOG(sprintf('STATUS : Calculating variance explained by regressors, looking at first %d components',... + min(SOM.NumberPrincipleComponents,size(PCScore,2)))); + +for iC = 1:min(SOM.NumberPrincipleComponents,size(PCScore,2)) + tmpData = SOM_RemoveMotion(theData,PCScore(:,1:iC)); + VARCOMP = [VARCOMP sum(var(tmpData,[],2))]; +end + +results.VARCOMP = VARCOMP; +results.PCScore = PCScore(:,1:min(SOM.NumberPrincipleComponents,size(PCScore,2))); +results.TOTALVAR = TOTALVAR; + +results.stopCPU = cputime; + +return diff --git a/som/SOM_ROIIDXnMASK.m b/som/SOM_ROIIDXnMASK.m new file mode 100755 index 00000000..8e494d37 --- /dev/null +++ b/som/SOM_ROIIDXnMASK.m @@ -0,0 +1,54 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% SOM_ROIIDXnMASK +% +% Determine the indices to the ROI that is in the mask that is provided +% +% function IDX = SOM_ROIIDXnMASK(parameters,ROIIDX) +% +% INPUT +% +% parameters = input to SOM_PreProcessData +% +% ROIIDX = linear indices of the above ROI +% +% +% OUTPUT +% +% IDX = indices of the ROI into the masking image. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function IDX = SOM_ROIIDXnMASK(parameters,ROIIDX) + +nVOXELS = prod(parameters.maskInfo.size); + +maskIMG = zeros(nVOXELS,1); +roiIMG = zeros(nVOXELS,1); + +% Create an array of the masking image. +maskIMG(parameters.maskInfo.iMask) = 1; + +% Create an array of the ROI image. +roiIMG(ROIIDX) = 1; + +% Take the product of the two such that a "1" is only there both the ROI +% and the masking image are present. + +combinedIMG = maskIMG.*roiIMG; + +% Now extract all bits from the combined image (the "1"'s and "0"'s). +roiBITS = combinedIMG(parameters.maskInfo.iMask); + +% Find the location of the surviving "1"'s. The values returned are the +% indices of the ROI in the mask. + +IDX = find(roiBITS); + +return + diff --git a/som/SOM_ReadMask.m b/som/SOM_ReadMask.m new file mode 100755 index 00000000..adfb22ac --- /dev/null +++ b/som/SOM_ReadMask.m @@ -0,0 +1,40 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2006 +% +% Routine to read time-series data. +% +% function [results, maskInfo] = SOM_ReadMask(PMask) +% +% P is array of file names (like that returned from spm_get) +% PMask is a file name for a binary mask. +% otherVoxels is an array of explicitly desired voxels. +% +% Only those voxels that are included in the mask are read. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results, maskInfo, analyzeFMT] = SOM_ReadMask(PMask) + +[fPath fName fExt] = fileparts(PMask); + +if strcmp(lower(fExt),'.img') == 1 + analyzeFMT = 1; +else + analyzeFMT = 0; +end + +if analyzeFMT == 1 + maskInfo.header = spm_vol(PMask); + maskInfo.analyzeFMT = 1; + results = spm_read_vols(maskInfo.header); +else + maskInfo.analyzeFMT = 1; + maskInfo.fPath = fPath; + load(PMask); +end + +maskInfo.iMask = find(results); + +return diff --git a/som/SOM_ReadNII.m b/som/SOM_ReadNII.m new file mode 100644 index 00000000..40442bb8 --- /dev/null +++ b/som/SOM_ReadNII.m @@ -0,0 +1,44 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% A routine to read the 4D file +% and shape to match the output of Luis' +% code +% +% function results = SOM_ReadNII(P); +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_ReadNII(P); + +results = -1; + +if exist(P) ~= 2 + SOM_LOG(sprintf('ERROR : File does not exist : %s',P)); + return +end + +try + DATA4D = nifti(P); +catch + SOM_LOG(sprintf('ERROR : Error reading file : %s',P)); + return +end + +if ndims(DATA4D.dat(:,:,:,:)) ~= 4 + SOM_LOG(sprintf('ERROR : %s does not appear to be time-series data',P)); + return +end + +results = reshape(DATA4D.dat(:,:,:,:),[prod(size(DATA4D.dat(:,:,:,1))) size(DATA4D.dat(:,:,:,:),4)])'; + +clear DATA4D; + +return + +% +% all done +% diff --git a/som/SOM_RemoveConfound.m b/som/SOM_RemoveConfound.m new file mode 100755 index 00000000..b9d5383f --- /dev/null +++ b/som/SOM_RemoveConfound.m @@ -0,0 +1,69 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% +% A routine to remove a confound from the data by regression +% +% +% theData = theData(space,time) (this is the +% standard format being used in this SOM +% implementation). +% +% theConfound = theConfound(time,1) +% (however, if the otherway the code will +% transpose for you.) +% +% +% function [results, b] = SOM_RemoveConfound(theData,theConfound) +% +% "results" is the new data with confound regressed away. +% +% "b" is the beta value (2xspace), first is beta +% confound, 2nd is beta for mean. +% +% See also SOM_RemoveMotion +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results, b] = SOM_RemoveConfound(theData,theConfound) + +% Transpose confound if need be. + +if size(theConfound,1) == 1 + theConfound = theConfound'; + SOM_LOG('WARNING : I had to transpose "theConfound"'); +end + +% Put the data in order of time x space. +% This is needed for solving the inverse +% problem. + +Y = theData'; + +% Make a simple design matrix. + +X = [theConfound ones(size(Y,1),1)]; + +% Get the fit to the data for the +% confound and the mean. + +b = inv(X'*X)*X'*Y; + +% Calculate the contribution due to the +% confound. + +Yp = X(:,1)*b(1,:); + +% Calculate the new data and put back in the form +% of space being the first dimension and time being +% the second. That is just remove the confound contribution. + +results = (Y-Yp)'; + +return + +% +% All done. +% diff --git a/som/SOM_RemoveMotion.m b/som/SOM_RemoveMotion.m new file mode 100755 index 00000000..1fadcaa3 --- /dev/null +++ b/som/SOM_RemoveMotion.m @@ -0,0 +1,90 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2006 +% +% +% A routine to remove motion confounds from the data by regression +% +% It is the same as SOM_RemoveConfound, just expanded to general +% number of confounds. Most likely could replace +% SOM_RemoveConfound. But will leave for now. +% +% theData = theData(space,time) (this is the +% standard format being used in this SOM +% implementation). +% +% theMotion = theConfound(time,[3 6]) +% +% you can also use more, such as the first derivatives. +% +% +% function [results, b] = SOM_RemoveConfound(theData,theMotion) +% +% "results" is the new data with confound regressed away. +% +% "b" is the beta value ([4 or 7]xspace), first 3/6 are beta +% for motion, last is beta for mean. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results, b] = SOM_RemoveMotion(theData,theMotion) + +% Check for number of motion parameters, just warn if not 3 or 6 + +if size(theMotion,2) ~= 3 | size(theMotion,2) ~= 6 | size(theMotion,2) ~= 9 | size(theMotion,2) ~= 12 + SOM_LOG('INFO : You have non-standard # of motion parameters, but continuing anyway.'); +end + +% Mean center the regressors + +SOM_LOG(sprintf('INFO : Motion Size : %d %d',size(theMotion,1),size(theMotion,2))); + +theMotionMeans = mean(theMotion,1); + +theMotionMeaned = theMotion; + +for iParam = 1:size(theMotionMeaned,2) + theMotionMeaned(:,iParam) = theMotionMeaned(:,iParam) - theMotionMeans(iParam); +end + +% Put the data in order of time x space. +% This is needed for solving the inverse +% problem. + +% Ok, normally we solve the GLM: +% +% Y = X * Beta +% +% however, our data is organized as time along columns, so +% we need to solve the equation: +% +% Y = Beta * X + +X = [theMotionMeaned';ones(1,size(theData,2))]; + +b = theData*X'*inv(X*X'); + +% Calculate the contribution due to the +% confound. + +Yp = b(:,1:end-1)*X(1:end-1,:); + +SOM_LOG(sprintf('INFO : Data/%d %d : Motion/%d %d : X/%d %d : b/%d %d : Yp/%d %d',... + size(theData,1),size(theData,2),... + size(theMotion,1),size(theMotion,2),... + size(X,1),size(X,2),size(b,1),size(b,2),size(Yp,1),size(Yp,2))); + +% Calculate the new data and put back in the form +% of space being the first dimension and time being +% the second. That is just remove the confound contribution. + +% And put data back in the right orientation. Space X Time. + +results = (theData - Yp); + +return + +% +% All done. +% diff --git a/som/SOM_Rho2Z.m b/som/SOM_Rho2Z.m new file mode 100755 index 00000000..d4794108 --- /dev/null +++ b/som/SOM_Rho2Z.m @@ -0,0 +1,19 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2007 +% +% A routine to transform Pearson's Rho to a Z value. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function Z = SOM_Rho2Z(Rho) + +Z = 1/2*log((1+Rho)./(1-Rho)); + +return + +% +% All done. +% + diff --git a/som/SOM_SetDefaults.m b/som/SOM_SetDefaults.m new file mode 100644 index 00000000..35e4d734 --- /dev/null +++ b/som/SOM_SetDefaults.m @@ -0,0 +1,53 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% +% +% Set the defaults +% +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function SOM_SetDefaults + +global SOM + +% Timing parameters + +SOM.defaults.TIME.TR = 2; %Seconds. +SOM.defaults.TIME.BandFLAG = 1; +SOM.defaults.TIME.TrendFLAG = 1; +SOM.defaults.TIME.LowF = 0.01; %Hz +SOM.defaults.TIME.HiF = 0.10; +SOM.defaults.TIME.padding = 10; +SOM.defaults.TIME.gentle = 1; +SOM.defaults.TIME.whichFilter = 1; +SOM.defaults.TIME.fraction = 1; + +% Some regression defaults, some, not all. + +SOM.defaults.RegressFLAGS.prinComp = 5; +SOM.defaults.RegressFLAGS.global = 0; +SOM.defaults.RegressFLAGS.order = 'DCWMB'; + +% ROI deaults + +SOM.defaults.roi.mni.size = 19; + +% Some file stuff. + +SOM.defaults.MaskImgThreshold = 0.75; + +% okay, done with that. + +SOM_LOG('STATUS : Setting defaults.'); + +% +% All done. +% + +return diff --git a/som/SOM_SpaceVerify.m b/som/SOM_SpaceVerify.m new file mode 100644 index 00000000..1f41c6cd --- /dev/null +++ b/som/SOM_SpaceVerify.m @@ -0,0 +1,69 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Validate that the mat and the dim of two headers are identical +% +% function OK = SOM_SpaceVerify(header_1,header_2) +% +% OK = -1 is bad +% +% = 1 all is okay. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function OK = SOM_SpaceVerify(header_1,header_2) + +OK = -1; + +% +% Did they pass headers? +% + +% Do this for convience so we can loop. +try + headers = {header_1(1),header_2(1)}; +catch + return +end + +fieldsToCheck = {'mat','dim'}; + +% Now check. + +for iHDR = 1:2 + for iField = 1:2 + if isfield(headers{iHDR},fieldsToCheck{iField}) == 0 + SOM_LOG('FATAL ERROR : You need to specify valid headers to be checked.'); + return + end + end +end + +% +% Okay they seem like valid headers, now check them. +% + +% We require the difference to be greater than "eps", built-in +% matlab variable. + +if any(abs((headers{1}.mat(:) - headers{2}.mat(:)))>eps) + SOM_LOG(sprintf('FATAL ERROR : ".mat(:)" does not match for files %s and %s ',headers{1}.fname,headers{2}.fname)); + return +end + +if any(headers{1}.dim(1:3) - headers{2}.dim(1:3)) + SOM_LOG(sprintf('FATAL ERROR : ".dim(1:3)" does not match for files %s and %s ',headers{1}.fname,headers{2}.fname)); + return +end + +OK = 1; + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_Sphere_Make.m b/som/SOM_Sphere_Make.m new file mode 100755 index 00000000..004dbba4 --- /dev/null +++ b/som/SOM_Sphere_Make.m @@ -0,0 +1,117 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh, Ben Kempke +% Copyright 2005-09 +% +% function [v diff_vect_h] = sphere_make(nPnts,nIter,plotOpt) +% +% Input : +% +% nPnts - number of charges on the surface +% nIter - number of iterations to minimize the energy +% plotOpt - option to plot the iterations - plotOpt = figure# +% +% Output : +% +% v - locations of the points on the sphere +% diff_vect_h = total summed distance that the points move. +% +% Calculate the locations of som examplar on the sphere surface. +% Do this by minimizing the mutual Coulomb iteraction on the surface. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [vn v thetav phiv diff_vect_h p3h] = SOM_Sphere_Make(nPnts,nIter,plotOpt) + +if exist('plotOpt') == 0 + plotOpt = 0; +end + +if isnumeric(plotOpt) == 0 + plotOpt = 0; +end + +if round(plotOpt) ~= plotOpt + plotOpt = 0; +end + +v = randn(nPnts,3); +v = SOM_UnitNormMatrix(v,2); + +% Put one on the pole and stick it there! + +v(1,:) = [0 0 1]; + +vn = v; + +thetav = []; +phiv = []; +diff_vect_h = []; +p3h = []; + +if nPnts < 2 + return +end + +weight = 1/nPnts; + +diff_vect = zeros(nPnts,nPnts,3); + +diff_vect_h = []; + +% This code fails if two charges are randomly assigned to the same space! + +if plotOpt > 0 + figure(plotOpt) + hold off; + subplot(211); + hold off; + sphere(40); + hold on; + p3h = plot3(v(:,1),v(:,2),v(:,3),'bo'); + set(p3h,'markerfacecolor','b'); + drawnow +end + +for ii=1:nIter + vdotv = v*v'; + % Potential energy is 1/r; + dist = (acos(vdotv)); + v_rep_x = repmat(reshape(v,[nPnts,1,3]),[1,nPnts,1]); + v_rep_y = repmat(reshape(v,[1,nPnts,3]),[nPnts,1,1]); + diff_vect = cross(v_rep_x,cross(v_rep_x,v_rep_y,3),3); + diff_vect = diff_vect./repmat(dist,[1,1,3]); + diff_vect = weight*diff_vect; + diff_vect(find(isnan(diff_vect))) = 0; + v_pert = squeeze(sum(diff_vect,2)); + diff_vect_h = [diff_vect_h sum(sum(sqrt(v_pert.*v_pert),2))]; + % the charge on the pole stays! + v(2:end,:) = SOM_UnitNormMatrix(v(2:end,:) + v_pert(2:end,:),2); + if plotOpt > 0 + figure(plotOpt) + subplot(211) + set(p3h,'xdata',v(:,1),'ydata',v(:,2),'zdata',v(:,3)); + %p3h = plot3(v(:,1),v(:,2),v(:,3),'b.'); + drawnow + subplot(223); + semilogy(diff_vect_h); + drawnow; + subplot(224); + bar(sum(sqrt(v_pert.*v_pert),2)); + drawnow; + end +end + +% Now we can sort them and number as they spiral away from the top of there sphere +% to the bottom of the sphere. + +thetav = acos(v(:,3)); +phiv = atan2(v(:,2),v(:,1)); +ip = find(phiv)< 0; +phiv(ip) = phiv(ip) + 2*pi; + +vn = sortrows([v thetav phiv],[ 4 5]); + +% +% All done +% diff --git a/som/SOM_SuperClust7.m b/som/SOM_SuperClust7.m new file mode 100755 index 00000000..02acdf0d --- /dev/null +++ b/som/SOM_SuperClust7.m @@ -0,0 +1,137 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh/Scott Peltier +% Copyright 2010 +% +% A routine to super cluster the SOM exemplars +% +% function [scmap_new,distmap]=SOM_SuperClust7(exemap,max_clustnum); +% +% max_clustnum is the maximum number of super clusters +% exemap is the examplar map which is on a grid and organized as +% xGrid x yGrid x Time +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [scmap_new,distmap]=SOM_SuperClust7(exemap,max_clustnum); + +xsize=size(exemap,1); +ysize=size(exemap,2); + +distmap=zeros(xsize,ysize,8); +scmap=zeros(xsize,ysize); + +clustnum=0; + +for x=1:xsize + for y=1:ysize + tc=squeeze(exemap(x,y,:)); + if ((x-1)<1) + distmap(x,y,[1 4 6])=NaN; + else + tcleft=squeeze(exemap(x-1,y,:)); + distmap(x,y,4)=sum((tc-tcleft).^2); + end; + if ((y-1)<1) + distmap(x,y,[1 2 3])=NaN; + else + tctop=squeeze(exemap(x,y-1,:)); + distmap(x,y,2)=sum((tc-tctop).^2); + end; + if ((x+1)>xsize) + distmap(x,y,[3 5 8])=NaN; + else + tcright=squeeze(exemap(x+1,y,:)); + distmap(x,y,5)=sum((tc-tcright).^2); + end; + if ((y+1)>ysize) + distmap(x,y,[6 7 8])=NaN; + else + tcbott=squeeze(exemap(x,y+1,:)); + distmap(x,y,7)=sum((tc-tcbott).^2); + end; + if (((x-1)>0) & ((y-1)>0)) + tculc=squeeze(exemap(x-1,y-1,:)); + distmap(x,y,1)=sum((tc-tculc).^2); + end; + if ((x0)) + tcurc=squeeze(exemap(x+1,y-1,:)); + distmap(x,y,3)=sum((tc-tcurc).^2); + end; + if (((x-1)>0) & (ymax_clustnum)) & (mloop<=length(xlist))) + orgx=xlist(mloop); + orgy=ylist(mloop); + nbnum=dirs(orgx,orgy,indx); + if (any(nbnum==[1 2 3])) + nbry=orgy-1; + elseif (any(nbnum==[6 7 8])) + nbry=orgy+1; + else + nbry=orgy; + end; + if (any(nbnum==[1 4 6])) + nbrx=orgx-1; + elseif (any(nbnum==[3 5 8])) + nbrx=orgx+1; + else + nbrx=orgx; + end; + if ((scmap(orgx,orgy)==0) & (scmap(nbrx,nbry)==0)) + clustnum=clustnum+1; + scmap(orgx,orgy)=clustnum; + scmap(nbrx,nbry)=clustnum; + elseif ((scmap(orgx,orgy)==0)) + scmap(orgx,orgy)=scmap(nbrx,nbry); + elseif ((scmap(nbrx,nbry)==0)) + scmap(nbrx,nbry)=scmap(orgx,orgy); + else + [gx,gy]=find(scmap==scmap(nbrx,nbry)); + for g=1:length(gx) + scmap(gx(g),gy(g))=scmap(orgx,orgy); + end; + end; + srtd_dist(orgx,orgy,indx)=NaN; + mloop=mloop+1; + end; + + end; %min_num +end; %i + +ulst=unique(scmap); +scmap_new=zeros(size(scmap)); + +for n=1:length(ulst) + [x,y]=find(scmap==ulst(n)); + for g=1:length(x) + scmap_new(x(g),y(g))=n; + end; +end; + +% +% All done +% \ No newline at end of file diff --git a/som/SOM_SuperClusterEasy.m b/som/SOM_SuperClusterEasy.m new file mode 100755 index 00000000..fed38409 --- /dev/null +++ b/som/SOM_SuperClusterEasy.m @@ -0,0 +1,109 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2006 +% +% Routine to make a superclusters +% +% function [results] = SOM_SuperClusterEasy(SelfOMap) +% +% Just call the SOM_CalculateMap again with the results. +% +% You should look at the matlab on how to use. Basically +% pass it the SOM calculation configuration as the global SOMSC +% You need to fill in all of the necessary parameters. +% +% You can control the super clustering with (defaults shown) +% +% global SOMSC +% +% SOMSC.oldMethod = 0 +% SOMSC.sigma = 0.001 +% SOMSC.sigmaTimeConstant = 1/4 +% SOMSC.learningTimeConstant = 2 +% SOMSC.alpha = 0.1 +% SOMSC.nSOM = 16 +% SOMSC.nIter = 25 +% +% You will want to have SOMSC.sigma = 0.001, else +% there is the risk that the super cluster smoothers +% the results. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results] = SOM_SuperClusterEasy(SelfOMap) + +global SOM +global SOMSC + +% Which method to determine neighborhood (same code, just cleaned +% up?). + +if ~isfield(SOMSC,'OldMethod') + SOMSC.OldMethod = 0; +end + +% Check to see if the necessary fields exist. + +% The neighborhood size +if ~isfield(SOMSC,'sigma') + SOMSC.sigma = .0001; + fprintf('SOMSC.sigma -> %f\n',SOMSC.sigma); +end + +% How quickly to modify the neighborhood size. +if ~isfield(SOMSC,'sigmaTimeConstant') + SOMSC.sigmaTimeConstant = 1/4; + fprintf('SOMSC.sigmaTimeConstant -> %f\n',SOMSC.sigmaTimeConstant); +end + +% How quickly to modify the map learning rate. +if ~isfield(SOMSC,'learningTimeConstant') + SOMSC.learningTimeConstant = 2; + fprintf('SOMSC.learningTimeConstant -> %f\n',SOMSC.learningTimeConstant); +end + +% The initial map learning rate. +if ~isfield(SOMSC,'alpha') + SOMSC.alpha = .1; + fprintf('SOMSC.alpha -> %f\n',SOMSC.alpha); +end + +% How big of a map. +if ~isfield(SOMSC,'nSOM') + SOMSC.nSOM = 16; + fprintf('SOMSC.nSOM -> %f\n',SOMSC.nSOM); +end + +% How many iterations. +if ~isfield(SOMSC,'nIter') + SOMSC.nIter = 25; + fprintf('SOMSC.nIter -> %f\n',SOMSC.nIter); +end + +% Store the current SOM settings. + +SOMS = SOM; + +% Now set them for supercluster calculation. + +SOM = SOMSC; + +% Call the SOM Calculate map function. + +% Use memory slot #2. + +results = SOM_CalculateMap(SelfOMap',SOM.nSOM,SOM.nIter,[],2); + +% Now reset the SOM parameters. + +SOM = SOMS; + +clear SOMS; + +% +% All done and return. +% + + + diff --git a/som/SOM_SuperClusterTC.m b/som/SOM_SuperClusterTC.m new file mode 100755 index 00000000..247b17c6 --- /dev/null +++ b/som/SOM_SuperClusterTC.m @@ -0,0 +1,96 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2010 +% +% A function to get the time courses for the +% super clusters. This will be based on a variety +% of methods +% +% function [SCTimeCourse] = SOM_SuperClusterTC(SOMResults,SCMap,whichOption) +% +% SOMResults - the structure that is returned by SOM_CalculateMap +% +% SCMap is a xGrid x yGrid array with super cluster memebership as value +% +% whichOption +% +% 1 = average the exemplars +% +% 2 = average the data +% +% 3 = weighted average of the data +% +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function SCTimeCourse = SOM_SuperClusterTC(SOMResults,SCMap,whichOption) + +global SOMMem + +SCTimeCourse = []; + +if isfield(SOMMem{1},'theData') == 0 + fprintf('Missing SOMMem\n'); + return +end + +if exist('whichOption') == 0 + whichOption = 1; +end + +if ismember(whichOption,[1 2 3]) == 0 + fprintf('Forcing whichOption to 1\n'); + whichOption = 1; +end + +if isfield(SOMResults,'SelfOMap') == 0 + fprintf('Missing SelfOMap as part of SOMResults structure\n'); + return +end + +if isfield(SOMResults,'IDX') == 0 + fprintf('Missing SelfOMap as part of SOMResults structure\n'); + return +end + +% Okay do the work now. + +SCUnique = unique(SCMap); + +nTime = size(SOMMem{1}.theData,2); + +SCTimeCourse = zeros(nTime,length(SCUnique)); + +NCHKVOXELCOUNT = 0; + +for iSC = 1:length(SCUnique) + iEXP = find(SCMap==SCUnique(iSC)); + IDXData = ismember(SOMResults.IDX,iEXP); + fprintf('Number in %03d is %03d and %05d\n',iSC,length(iEXP),length(find(IDXData))); + NCHKVOXELCOUNT = NCHKVOXELCOUNT + length(find(IDXData)); + switch whichOption + case 1 + % Average of the exemplars + SCTimeCourse(:,iSC) = mean(SOMResults.SelfOMap(:,iEXP),2); + fprintf('using exemplars\n'); + case 2 + % Average of the data in that SC. + SCTimeCourse(:,iSC) = mean(SOMMem{1}.theData(IDXData,:),1)'; + fprintf('using data\n'); + case 3 + fprintf('Not implemented yet.\n'); + end + +end + +SCTimeCourse = SOM_UnitNormMatrix(SCTimeCourse,1); + +% +% All done. +% + + + + + diff --git a/som/SOM_TTest.m b/som/SOM_TTest.m new file mode 100755 index 00000000..5e399154 --- /dev/null +++ b/som/SOM_TTest.m @@ -0,0 +1,96 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2007 +% +% +% A routine to calculate a one-sample or two-sample t-test. +% +% It is assumed that there is equal variance, however, this +% can be overridden with options. NOT IMPLEMENTED YET. +% +% results = SOM_TTest(DataSample1,[DataSample2]); +% +% Input : +% +% DataSample1 = DataSample1(nVoxels,ns1) +% DataSample2 = DataSample2(nVoxels,ns2) +% +% +% Output: +% +% TMap = TMap(Voxels) +% Nu - Number of degree of freedom. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [TMap Nu] = SOM_TTest(DataSample1,varargin) + +TMap = []; +Nu = []; + +% Get the first group information. + +nVoxels1 = size(DataSample1,1); +n1 = size(DataSample1,2); + +if nargin > 2 + fprintf('You have passed too many arrays.\n'); + return +end + +% Is there a second group available. + +if nargin == 1 + TTest = 1; + % + % Make sure we have enough subejcts to do T-Test. + % + if n1 < 3 + fprintf('You need to give me at least 3 subjects.\n'); + return + end +else + if isnumeric(varargin{1}) + nVoxels2 = size(varargin{1},1); + n2 = size(varargin{1},2); + if any ( [nVoxels1] - [nVoxels2] ) + fprintf('Error, you have specified data arrays with different number of voxels.\n'); + return + end + if n2 < 2 | n1 < 2 + fprintf('You need to have at least 2 subjects per group.\n'); + return + end + TTest = 2; + DataSample2 = varargin{1}; + end +end + +switch TTest + % + % One sample t-test + % + case {1} + G1Var = var(DataSample1,[],2); + G1Mu = mean(DataSample1,2); + TMap = G1Mu./sqrt(G1Var/n1); + Nu = n1-1; + % + % Two sample t-test + % + case {2} + G1Var = var(DataSample1,[],2); + G2Var = var(DataSample2,[],2); + G1Mu = mean(DataSample1,2); + G2Mu = mean(DataSample2,2); + TMap = (G1Mu-G2Mu) ./ ... + sqrt((G1Var*(n1-1)+G2Var*(n2-1))/(n1+n2-2)*(1/n1+1/n2)); + Nu = n1+n2-2; +end + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_TestMEX.c b/som/SOM_TestMEX.c new file mode 100755 index 00000000..cee7fdd6 --- /dev/null +++ b/som/SOM_TestMEX.c @@ -0,0 +1,380 @@ +/*----------------- +% +% Copyright Robert C. Welsh, Ann Arbor, MI, 2006 +% +% A routine to calculate the cost metrix +% between U and V where U are data (nVoxels,nTime) and +% V is SOM Exemplars (nTime,nVoxels); +% +% results = SOM_CostFunction(theData,SelfOMap,[whichCOST]); +% +% Input: +% +% theData = theData(nVoxels,nTime) +% SelfOMap = SelfOMap(nTime,nSOM); +% +% Optional: +% +% whichCOST = 0 - U.V (opening angle) (default) +% 1 - |U-V| (normalized euclidean distance). +% 2 - |U-V|^2 +% 3 - Mutual Information +% Output: +% +% results = results(nVoxels,nSOM) -> Cost Function. +% +% To compile do: +% +% mex [-DSOMDEBUG] [-DSOMDEBUG2] SOM_CostFunction.c +% +% where -DSOMDEBUG[2] is a debug flag to the compiler. +% +% +% -Robert Welsh, 2006-12-12. +% +%------------------*/ + +#include +#include "mex.h" + +/*// Define a ^2 function to use in Euclidean Distance to avoid using math routine "pow", it's too slow!*/ +#define SQR(a) (a*a) + +#define EPS 1e-10 + +/* + Exit routine to be called with the routine is cleared. +*/ + +static void SOM_ExitCost(void) +{ + mexPrintf("Called exit routine : 'SOM_ExitCost'\n"); +} + +/* + + Routine to calculate the Mutual Inforation Cost Function. + Based loosely on some code from Luis Hernandez. + + *** WARNING *** Mutual Information calculation is really slow!!! + +*/ + +float SOM_MutualInformation(double data[], double som[], int npnts) +{ + + int iPnt; + + double minx, maxx, deltax, miny, maxy, deltay; + + int nx, nxx; + + int iX, iY; + + double MI; + + /* Dynamically create the histograms: joint, x, and y */ + + double *JH, *HX, *HY; + + double histIncr; + + /* Make sure we have enough data points to histogram */ + + if (npnts < 2) + { + mexPrintf("The number of times points < 2, silly.\n"); + mexErrMsgTxt("Aborting."); + }; + + /* Ok, determine limits of histograms for calculating entropy */ + + minx = 1e9; + miny = 1e9; + maxx = -1e9; + maxy = -1e9; + + /* Find the limits of the histograms. */ + + for (iPnt = 0;iPnt < npnts;iPnt++) + { + minx = ( (minxdata[iPnt]) ? maxx : data[iPnt] ); + maxy = ( (maxx>som[iPnt]) ? maxy : som[iPnt] ); + } + + /* Determine number of elements in each histogram direction */ + + nx = (int) ( pow( (float)npnts , (float)(1./3.) ) + .5); + + deltax = (maxx-minx)/(nx-1); + deltay = (maxy-miny)/(nx-1); + + /* now add a bin for underflows and overflows */ + + nxx = nx + 2; + + if ( nx < 2 ) + { + mexPrintf("\nSOM_CostFunction.c:\n Histogram size needs to be at least 2x2\n\n"); + mexErrMsgTxt("Aborting."); + } + + /* Request memory from matlab heap for histograms */ + + JH = mxCalloc((nxx*nxx),sizeof(double)); + HX = mxCalloc(nxx,sizeof(double)); + HY = mxCalloc(nxx,sizeof(double)); + + /* Accumulate the histograms, use the following + for the histogram increment, that way it will be + unit normalized already */ + + histIncr = (double) (1.0/( (double) npnts)); + + for (iPnt = 0; iPnt < npnts; iPnt ++) + { + /* Calculate index and min and max it.*/ + + iX = (int) ((data[iPnt]-minx)/deltax + 1.5); + iY = (int) ((som[iPnt]-miny)/deltay + 1.5); + +#if SOMDEBUG2 + mexPrintf("%d %d\n",iX,iY); +#endif + + iX = (iX < 0) ? 0 : iX; + iY = (iY < 0) ? 0 : iY; + + iX = (iX > nx+1) ? nx+1 : iX; + iY = (iY > nx+1) ? nx+1 : iY; + + HX[iX] += histIncr; + HY[iY] += histIncr; + JH[iX*nxx+iY] += histIncr; + } + +#if SOMDEBUG2 + for (iX = 0; iX <= nx+1; iX ++) + { + for (iY = 0; iY <= nx+1 ; iY++) + mexPrintf("%f ",JH[iX*nxx+iY]); + mexPrintf("\n"); + } + mexPrintf("\n"); +#endif + + /* Now calculate the entropy, include overflow and underflow bins */ + + MI = 0; + + for (iX = 0; iX < nxx; iX ++) + for (iY = 0; iY < nxx ; iY++) + if ( HX[iX] > 0 && HY[iY] > 0 ) + { +#if SOMDEBUG2 + mexPrintf("%f %f %f %d %d\n",JH[iX*nxx+iY],HX[iX],HY[iY],iX,iY); +#endif + MI += JH[iX*nxx+iY]*log( ( (JH[iX*nxx+iY]+EPS)/HX[iX]/HY[iY] ) ); + } + +#if SOMDEBUG + mexPrintf("min/max : %f %f %f\n",minx,maxx,deltax); + mexPrintf("min/max : %f %f %f\n",miny,maxy,deltay); + mexPrintf("\n"); +#endif + + /* Free up the memory */ + + mxFree(JH); + mxFree(HX); + mxFree(HY); + +#if SOMDEBUG + mexPrintf("MI : %f\n",MI); +#endif + + return MI; + +} + +void SOM_CostFunctionUsage() +{ + mexPrintf("\nUsage : results = SOM_CostFunction(theData,SelfOMap,whichCOST)\n\n"); + mexPrintf(" theData -> theData(nVoxels,nTime)\n"); + mexPrintf(" SelfOMap -> theData(nTime,nSOM)\n"); + mexPrintf(" whichCOST -> 0=U.V, 1=|U-V|, 2=|U-V|^2, 3=Mutual Information\n\n"); +} + +/* + This is the main function that is called by MATLAB + + The routine will return a matrix of the cost-function evaluation. + + All of the cost functions are contained in here, except mutual information + which is lengthy code-wise and hence is above. + +*/ + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + mxArray *theDataMX; + mxArray *SelfOMapMX; + + mxArray *theDataTMX; /* Needed for mutual information calculation.*/ + + mxArray *tmpMX[2]; + + mxArray *whichCOSTMX; + + mxArray *resultstMX; + double *resultst; + + double *whichCOST; + + double *results; + + double *theData; + double *SelfOMap; + + double *theDataT; /* Needed for mutual information */ + + int nTime; + int nTimeSOM; + + int nSOM; + int nVoxels; + + int iSOM; + int iTime; + int iVoxel; + + int i1; + + int iPnt; + int COSTFLAG; + + double tmp1; + double tmp2; + + int idx1; + int idx2; + + /*// Now the function.*/ + + /*// Did they pass in enough arguements?*/ + + COSTFLAG = 1; + + mexAtExit(SOM_ExitCost); + + if (nrhs < 2 | nrhs > 3) + { + SOM_CostFunctionUsage(); + mexErrMsgTxt("Error, wrong number of input parameters."); + } + + /*// get the cost function option. 0 = U.V, 1 = |U-V|*/ + + if (nrhs == 3) + { + whichCOSTMX = prhs[2]; + if (mxGetM(whichCOSTMX) != 1 | mxGetN(whichCOSTMX) != 1) + { + SOM_CostFunctionUsage(); + mexErrMsgTxt("Error, 'whichCOST' must be a scaler."); + } + whichCOST = mxGetPr(whichCOSTMX); + switch ((int) whichCOST[0]) + { + case 3: + { +#if SOMDEBUG + mexPrintf("Using mutual information cost function.\n"); +#endif + COSTFLAG=3; + } + break; + case 2: + { +#if SOMDEBUG + mexPrintf("Using |U-V|^2 cost function.\n"); +#endif + COSTFLAG=2; + } + break; + case 1: + { +#if SOMDEBUG + mexPrintf("Using |U-V| cost function.\n"); +#endif + COSTFLAG=1; + } + break; + default: + { +#if SOMDEBUG + mexPrintf("Using U.V cost function.\n"); +#endif + COSTFLAG=0; + } + break; + } + } + + /*// Get the pointers to the data and self-organizing map.*/ + + theDataMX = prhs[0]; + SelfOMapMX = prhs[1]; + + /*// Now get the dimensions of each.*/ + + nVoxels = mxGetM(theDataMX); + nTime = mxGetN(theDataMX); + + nTimeSOM = mxGetM(SelfOMapMX); + nSOM = mxGetN(SelfOMapMX); + + theData = mxGetPr(theDataMX); + + SelfOMap = mxGetPr(SelfOMapMX); + + /*// Make sure the dimensions are good.*/ + if (nTime != nTimeSOM) + { + SOM_CostFunctionUsage(); + mexPrintf("Error, time points in 'SelfOMap' and time points of 'theData' don't match!\n"); + mexErrMsgTxt("Aborting."); + }; + +#if SOMDEBUG + mexPrintf("theData(%d,%d), SelfOMap(%d,%d)\n",nVoxels,nTime,nTimeSOM,nSOM); + mexPrintf("number of returns : %d\n",nlhs); +#endif + + /*// Results returned in a new matrix.*/ + + /* + plhs[0] = mxCreateDoubleMatrix(nVoxels,nSOM,mxREAL); + */ +#if SOMDEBUG + /* mexPrintf("Created output array of size %d x %d\n",mxGetM(plhs[0]),mxGetN(plhs[0])); +*/ +#endif + + /*// get the pointer to the data area of the output array.*/ + + /* results = mxGetPr(plhs[0]); + */ + /*// Now run the cost function.*/ + +#if SOMDEBUG + mexPrintf("COSTFLAG:%d\n",COSTFLAG); +#endif +} + + +/*// All done.*/ + diff --git a/som/SOM_Texturizer.m b/som/SOM_Texturizer.m new file mode 100755 index 00000000..267e84cb --- /dev/null +++ b/som/SOM_Texturizer.m @@ -0,0 +1,16 @@ +function SOM_Texturizer(results_initial, results_supercluster) + +%not gonna work with a grid +if ~isfield(results_initial,'v2') + fprintf('Did not run for a sphere!') + return +end + +%texturize the sphere with the given vertices... still needs some cleaning up... +[b,v,t] = texturizer_v4(results_initial.v2); + +%vertex colors are determined by which cluster they belong to +y = results_supercluster.IDX'; + +%draw the overly-graphically-intensive spheres +sphere_hist; diff --git a/som/SOM_UnitNormMatrix.m b/som/SOM_UnitNormMatrix.m new file mode 100755 index 00000000..5fa7b64b --- /dev/null +++ b/som/SOM_UnitNormMatrix.m @@ -0,0 +1,56 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% A function to norm a matrix along a given direction. +% +% This is for the SOM ToolBox. +% +% theMatrix = theMatrix(dim1,dim2); +% whichType = 1 for dim1 = time dimension +% dim2 = space dimension +% +% = 2 for dim1 = space dimension +% dim2 = time dimension +% +% +% Version 1.0 +% +% function results = SOM_UnitNormMatrix(theMatrix,whichType) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_UnitNormMatrix(theMatrix,whichType) + +%if ndims(theMatrix) ~= 2 || (ndims(theMatrix) == 2 && size(theMatrix,1) == 1 ) +% results = theMatrix; +% fprintf('This is specifically for 2 dim data [time and space]\n'); +% return +%end + +M2 = theMatrix.*theMatrix; + +SM2 = sum(M2,whichType); + +if whichType == 2 + SM2 = SM2'; +end + +SM = SM2.^(1/2); + +NM = ones(size(theMatrix,whichType),1)*SM; + +if whichType == 2 + NM = NM'; +end + +results = theMatrix./(NM+.000001); + if(any(any(isnan(results)))) + keyboard; + end +return + +% +% All done. +% diff --git a/som/SOM_VarianceMap.m b/som/SOM_VarianceMap.m new file mode 100755 index 00000000..9946fa16 --- /dev/null +++ b/som/SOM_VarianceMap.m @@ -0,0 +1,60 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% +% +% A program to calculate the variance map of the +% time series date. +% +% function [results] = SOM_VarianceMap(theData,maskInfo) +% +% theData - space x time array of fcMRI data +% maskInfo - structure used for the mask. See SOM_PrepData +% +% A Variance Map will be written in the source directory with +% the name of "variance.img" +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +function [results, varHdr] = SOM_VarianceMap(theData,maskInfo) + +% Calculate the variance + +% Modified on 2007-3-29 to speed up the code and reduce memory +% load. R.C. Welsh + +varMap = var(theData,[],2); + +% Create a dummy volume of correct dimensions. + +varVol = zeros(maskInfo.header.dim(1:3)); + +% Fill the values. + +varVol(maskInfo.iMask) = varMap; + +if maskInfo.analyzeFMT == 1 + % Create a new header. + + varHdr = maskInfo.header; + + [pn pf] = fileparts(varHdr.fname); + + varHdr.fname = fullfile(pn,'variance.img'); + + % Write the analyze image. + + spm_write_vol(varHdr,varVol); +else + save(fullfile(maskInfo.fPath,'varMap')','varMap'); +end + +results = varMap; + +return + +% +% All done +% \ No newline at end of file diff --git a/som/SOM_ViewMap.m b/som/SOM_ViewMap.m new file mode 100755 index 00000000..d945beac --- /dev/null +++ b/som/SOM_ViewMap.m @@ -0,0 +1,42 @@ + +PAnatomy = spm_get([0 1],'*.img','Pick the anatomy'); + +if length(PAnatomy) < 0 + return +end + +PSOM = spm_get([0 1],'*.img','Pick the SOM Image'); + +if length(PSOM) < 0 + return +end + +SOMThreshold = spm_input('Threshold','+1','r',[0],1); + +spm_figure('Create','Graphics'); + +Fgraph = spm_figure('FindWin','Graphics'); +spm_results_ui('Clear',Fgraph); +spm_orthviews('Reset'); + +global st +st.Space = spm_matrix([0 0 0 0 0 -pi/2])*st.Space; + +spm_orthviews('Image',PAnatomy); + +spm_orthviews MaxBB; + +%spm_orthviews('register',hReg); + +VOL = spm_read_vols(spm_vol(PSOM)); + +XYZ = SOM_XYZ(PSOM); + +VOLIDX = find(VOL>=SOMThreshold); +VOLZ = VOL(VOLIDX); +VOLXYZ = XYZ(:,VOLIDX); +VOLHDR = spm_vol(PSOM); + +spm_orthviews('addblobs',1,VOLXYZ,VOLZ,VOLHDR.mat); + +spm_orthviews('Redraw'); diff --git a/som/SOM_Weighted_SUM.m b/som/SOM_Weighted_SUM.m new file mode 100755 index 00000000..7e730d5d --- /dev/null +++ b/som/SOM_Weighted_SUM.m @@ -0,0 +1,37 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% Ann Arbor MI. +% +% function results = SOM_Weighted_SUM(SOMResults) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function [results, AWT] = SOM_Weighted_SUM(SOMResults) + +WTS = []; +NUM = []; + +for iSOM = 1:size(SOMResults.SOM,2) + ii = find(SOMResults.IDX==iSOM); + NUM = [NUM length(ii)]; + if length(ii) > 0 + theWTS = SOMResults.WTS(ii); + else + theWTS = 0; + end + WTS = [WTS sum(theWTS)]; +end + +wtMASK = (NUM>0); + +AWT = wtMASK.*(WTS./(NUM.*wtMASK+(1-wtMASK))); + +results = WTS; + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_WriteData.m b/som/SOM_WriteData.m new file mode 100755 index 00000000..80623457 --- /dev/null +++ b/som/SOM_WriteData.m @@ -0,0 +1,84 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005-2007 +% +% Routine to write time-series data. +% +% function [results] = SOM_WriteData(theData,maskInfo,volumeWild) +% +% Write out the data from the Space x Time array using the +% volume information contained in maskInfo and the name prefix +% from volumeWild +% +% theData = theData(nSpace,nTime); +% +% maskInfo.iMask = indices back into 3-D array. +% +% further maskInfo can be 1 of two types +% +% SPM style information +% +% maskInfo +% .analyzeFMT == 1 +% .header - spm style header +% +% Plain ".mat" style +% +% .analyzeFMT = 0 (or not 1) +% .dim = [x y z] voxels +% .path = path where data should go. +% .output = 3 - for 3d files, 4 - for 4d file. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_WriteData(theData,maskInfo,volumeWild); + +fprintf('Writing Data\n\n'); + +if maskInfo.analyzeFMT == 1 + genHdr = maskInfo.header; + [genDir genNam] = fileparts(genHdr.fname); + vDIM = maskInfo.header.dim; + maskInfo.output = 3; +else + genDir = maskInfo.path; + vDIM = maskInfo.dim; + if isfield(maskInfo,'output') == 0 + fprintf('Going to write time-series as 3D mats.\n'); + maskInfo.output = 3; + end +end + +% Write out a 3D or 4D file. + +if maskInfo.output == 3 + Volume = zeros(vDIM); + for iP = 1:size(theData,2); + Volume = 0*Volume; + Volume(maskInfo.iMask) = theData(:,iP); + if maskInfo.analyzeFMT == 1 + volHdr = genHdr; + volHdr.fname = fullfile(genDir,sprintf('%s%04d.img',volumeWild,iP)); + volHdr.pinfo = [1;0;0]; + spm_write_vol(volHdr,Volume); + else + save(fullfile(genDir,sprintf('%s%04d',volumeWild,iP)),'Volume'); + end + end +else + Volume = zeros(prod(vDIM),size(theData,2)); + Volume(maskInfo.iMask,:) = theData; + Volume = reshape(Volume,[vDIM size(theData,2)]); + save(fullfile(genDir,sprintf('%s_4D',volumeWild)),'Volume'); +end + +fprintf('\nDone\n'); +clear Volume; +clear theData; + +return + +% +% All done. +% \ No newline at end of file diff --git a/som/SOM_WriteIMGS.m b/som/SOM_WriteIMGS.m new file mode 100755 index 00000000..4f0cefe9 --- /dev/null +++ b/som/SOM_WriteIMGS.m @@ -0,0 +1,42 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% Ann Arbor MI. +% +% function results = SOM_Write(SOMResults) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_WriteIMGS(SOMResults) + +curDIR = pwd; + +wts = SOM_Weighted_SUM(SOMResults); + +[oWTS oI] = sort(wts); + +iIMG = 0; + +for ix = 100:-1:96 + iIMG = iIMG + 1; + hdr = SOMResults.header; + hdr.fname = fullfile(curDIR,sprintf('som_%03d.img',iIMG)); + hdr.dim(4) = 64; + hdr.pinfo = [1;0;0]; + vol = zeros(hdr.dim(1:3)); + vol(:,:,:) = nan; + ii = find(SOMResults.IDX == oI(ix)); + vol(SOMResults.iMask(ii)) = SOMResults.WTS(ii); + spm_write_vol(hdr,vol); +end + +clear vol +clear SOMResults + +return + +% +% All done. +% + diff --git a/som/SOM_WriteIMGSByAvg.m b/som/SOM_WriteIMGSByAvg.m new file mode 100755 index 00000000..d0a38510 --- /dev/null +++ b/som/SOM_WriteIMGSByAvg.m @@ -0,0 +1,48 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% Ann Arbor MI. +% +% function results = SOM_Write(SOMResults) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_WriteIMGSByAvg(SOMResults) + +curDIR = pwd; + +[wts awt] = SOM_Weighted_SUM(SOMResults); + +[oWTS oI] = sort(awt); + +iIMG = 0; + +for ix = size(SOMResults.SOM,2):-1:max([1 size(SOMResults.SOM,2)-9]) + iIMG = iIMG + 1; + if SOMResults.maskInfo.analyzeFMT == 1 + hdr = SOMResults.header; + hdr.fname = fullfile(curDIR,sprintf('som_byAvg_%03d.img',iIMG)); + hdr.pinfo = [1;0;0]; + somMap = zeros(hdr.dim(1:3)); + else + somMap = zeros(SOMResults.maskInfo.size); + end + somMap(:,:,:) = nan; + ii = find(SOMResults.IDX == oI(ix)); + somMap(SOMResults.iMask(ii)) = SOMResults.WTS(ii); + if SOMResults.maskInfo.analyzeFMT == 1 + spm_write_vol(hdr,somMap); + else + save(fullfile(SOMResults.maskInfo.fPath,sprintf('som_byAvg_%03d',iIMG)),'somMap'); + end +end +clear SOMResults +clear somMap + +return + +% +% All done. +% + diff --git a/som/SOM_WriteIMGSByMask.m b/som/SOM_WriteIMGSByMask.m new file mode 100755 index 00000000..b4cd4662 --- /dev/null +++ b/som/SOM_WriteIMGSByMask.m @@ -0,0 +1,76 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2005 +% Ann Arbor MI. +% +% function results = SOM_WriteByMask(SOMResults) +% +% Take the order from the most populous map to the least +% and look for activation according to the mask. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_WriteIMGSByMask(SOMResults,maskingVol,name_extra) + +if exist('name_extra') ~= 1 + name_extra = 'byMask'; +end + +curDIR = pwd; + +[wts awt] = SOM_Weighted_SUM(SOMResults); + +[oWTS oI] = sort(awt); + +iIMG = 0; + +if SOMResults.maskInfo.analyzeFMT == 1 + % Get the header. + hdr = SOMResults.header; + % Make the temp volume. + somMap = zeros(hdr.dim(1:3)); +else + somMap = zeros(SOMResults.maskInfo.size); +end + +for ix = size(SOMResults.SOM,2):-1:1 + % Assume all is nan. + somMap(:,:,:) = nan; + % Fill with the results. + ii = find(SOMResults.IDX == oI(ix)); + somMap(SOMResults.iMask(ii)) = SOMResults.WTS(ii); + % Make a mask of the valid numbers. + nonNANSomMap = 1-isnan(somMap); + % Are there values in the mask that are non-nans? + if sum(sum(sum(maskingVol.*nonNANSomMap))) > 0 + iIMG = iIMG + 1; + if SOMResults.maskInfo.analyzeFMT == 1 + hdr = SOMResults.header; + % Make the name the order it was written and which examplar it + % belongs. + hdr.fname = fullfile(curDIR,sprintf('som_%s_%03d_%03d.img',name_extra,iIMG,oI(ix))); + hdr.pinfo = [1;0;0]; + spm_write_vol(hdr,somMap); + else + save(fullfile(curDIR,sprintf('som_%s_%03d_%03d',name_extra,iIMG,oI(ix))),'somMap'); + end + else + fprintf('Image is empty : %s\n',name_extra); + end +end + +fprintf('Wrote %d images.\n',iIMG); + +results = iIMG; + +clear SOMResults +clear somMap +clear maskingVol + +return + +% +% All done. +% + diff --git a/som/SOM_WriteNII.m b/som/SOM_WriteNII.m new file mode 100644 index 00000000..d4d7b547 --- /dev/null +++ b/som/SOM_WriteNII.m @@ -0,0 +1,100 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% A routine to more easily write out nifti files +% using the spm routines but adding a layer to copy +% the head of a template image. +% +% Input +% +% TemplateImageName +% NewName +% Volume (3D or 4D) +% +% function results = SOM_WriteNII(TemplateImage,NewName,Volume) +% +% Default is to write in the same directory, have to test +% to see if name can include directory path. +% +% function results = SOM_WriteNII(TemplateImage,NewName,Volume,dtype) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_WriteNII(TemplateImage,NewName,Volume,dtype) + +results = -1; + +% First get the dimensions of what we want to write, and we need to +% pad with 1's for 3D to become the proper order. + +volDIM = size(Volume); + +if length(volDIM) < 4 + volDIM = [volDIM 1]; +end + +try + niftiIn = nifti(TemplateImage); +catch + SOM_LOG(sprintf('FATAL ERROR : TemplateImage can''t be read : %s\n',TemplateImage)); + return +end + +if any(volDIM(1:3)-size(niftiIn.dat(:,:,:,1))) + SOM_LOG(sprintf('FATAL ERROR : TemplateImage dimension doesn''t match data dimension to be written\n')); + return +end + +% data area. +niftiOutData = file_array; + +niftiOutData.fname = NewName; +niftiOutData.dim = volDIM; + +% No error checking on that they pass in!!! +if exist('dtype') == 0 + niftiOutData.dtype = niftiIn.dat.dtype; +else + niftiOutData.dtype = dtype; +end + +niftiOutData.offset = niftiIn.dat.offset; + +% header area +niftiOut = nifti; +niftiOut.mat = niftiIn.mat; +niftiOut.mat_intent = niftiIn.mat_intent; +niftiOut.mat0 = niftiIn.mat0; +niftiOut.mat0_intent = niftiIn.mat0_intent; +niftiOut.descrip = [niftiIn.descrip ', SOM Create Nifti file']; +niftiOut.timing = niftiIn.timing; + +% Clear the hook to the template image. +clear niftiIn; + +% Put the data onto disk. +niftiOut.dat = niftiOutData; + +% create the file. +create(niftiOut); + +% Actually write the data now, first reserving some zeros. -- Not sure if we +% need to do this step. +niftiOut.dat(:,:,:,:) = zeros(niftiOutData.dim); + +% data to disk. +niftiOut.dat(:,:,:,:) = Volume; + +% Close our connection to the output file. +clear niftiOut + +% All done + +results = NewName; + +return + diff --git a/som/SOM_corr4D.m b/som/SOM_corr4D.m new file mode 100644 index 00000000..33245363 --- /dev/null +++ b/som/SOM_corr4D.m @@ -0,0 +1,27 @@ + +function corrMAP = SOM_corr4D(vol1,vol2) + +if any(size(vol1) - size(vol2)) + fprintf('No identical\n'); + corrMAP = []; + return +end + +corrMAP = zeros(size(squeeze(vol1(:,:,:,1)))); + +idx = find(diag(ones(prod(size(squeeze(vol1(:,:,1,1)))),1))); + +nX = size(vol1,1); +nY = size(vol1,2); +nZ = size(vol1,3); +nXY = nX*nY; +nTIME = size(vol1,4); + +for iZ = 1:size(vol1,3) + data1 = vol1(:,:,iZ,:); + data2 = vol2(:,:,iZ,:); + data1 = reshape(data1,[nXY nTIME])'; + data2 = reshape(data2,[nXY nTIME])'; + cmap = corr(data1,data2); + corrMAP(:,:,iZ) = reshape(cmap(idx),[nX nY]); +end diff --git a/som/SOM_editTimeSeries.m b/som/SOM_editTimeSeries.m new file mode 100644 index 00000000..830aacc2 --- /dev/null +++ b/som/SOM_editTimeSeries.m @@ -0,0 +1,41 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Routine to take 2D input (space x time) +% and edit out points +% +% function D1 = SOM_editTimeSeries(D0,censorVector) +% +% data = space x time data. +% +% censorVector = string of 1's and 0's, one element per time +% point on what to keep and throw away in +% time-series data. +% +% Output +% +% D1 = space x time data, edited. +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function D1 = SOM_editTimeSeries(D0,censorVector) + +D1 = -1; + +if size(D0,2) ~= length(censorVector) + SOM_LOG(sprintf('FATAL : Size of D0 (%d,%d) not consistent with edit vector (%d))',size(D0,1),size(D0,2),length(censorVector))); + return +end + +idxGood = find(censorVector~=0); +D1 = D0(:,idxGood); + +return + +% +% all done +% \ No newline at end of file diff --git a/som/SOM_img_endian.m b/som/SOM_img_endian.m new file mode 100755 index 00000000..6b06c6c5 --- /dev/null +++ b/som/SOM_img_endian.m @@ -0,0 +1,39 @@ +% +% Borrowed from Luis Hernandez +% +% Robert Welsh +% 2011 +% + +function endian = SOM_img_endian(name) +% function endian = SOM_img_endian(name) + +[pFile,messg] = fopen(name, 'r','native'); +if pFile == -1 + fprintf('Error opening header file: %s',name); + return; +end +tmp = fread(pFile,1,'int32'); + +if strcmp(computer,'GLNX86') | ... + strcmp(computer , 'PCWIN') | ... + strcmp(computer , 'GLNXA64') | ... + strcmp(computer , 'MACI') | ... + strcmp(computer , 'MACI64') + + if tmp==348 + endian='ieee-le'; + else + endian='ieee-be'; + end + +else + if tmp==348 + endian='ieee-be'; + else + endian='ieee-le'; + end + +end + +return diff --git a/som/SOM_read_nii_hdr.m b/som/SOM_read_nii_hdr.m new file mode 100755 index 00000000..4c87e5f6 --- /dev/null +++ b/som/SOM_read_nii_hdr.m @@ -0,0 +1,119 @@ +% +% Borrowed from Luis Hernandez +% +% Robert Welsh +% 2011 +% + +function hdr=SOM_read_nii_hdr(name) +%function hdr=read_nii_hdr(name) + +suffix = name(end-2:end); +if strcmp(suffix,'.gz') + eval(['!gunzip ' name]); + name = name(1:end-3); +elseif strcmp(suffix,'nii') + name = name; +% else +% name = sprintf('%s.nii',name); +end + + % first detect which endian file we're opening + [pFile,messg] = fopen(name, 'r','native'); + if pFile == -1 + fprintf('Error opening header file: %s',name); + return; + end + + tmp = fread(pFile,1,'int32'); + + if strcmp(computer,'GLNX86') | ... + strcmp(computer , 'PCWIN')| ... + strcmp(computer,'GLNXA64') |... + strcmp(computer,'MACI') | ... + strcmp(computer,'MACI64') + if tmp==348 + endian='ieee-le'; + else + endian='ieee-be'; + end + %fprintf('%s %s\n',computer,endian); + else + if tmp==348 + endian='ieee-be'; + else + endian='ieee-le'; + end + fprintf('%s %s\n',computer,endian); + end + + fclose(pFile); + % Now Read in Headerfile into the hdrstruct + [pFile,messg] = fopen(name, 'r', endian); + if pFile == -1 + msgbox(sprintf('Error opening header file: %s',name)); + return; + end + + +hdr = struct (... + 'sizeof_hdr' , fread(pFile, 1,'int32')',... % should be 348! + 'data_type' , (fread(pFile,10,'*char')'),... + 'db_name' , (fread(pFile,18,'*char')'),... + 'extents' , fread(pFile, 1,'int32')', ... + 'session_error' , fread(pFile, 1,'int16')', ... + 'regular' , fread(pFile, 1,'*char')', ... + 'dim_info' , fread(pFile, 1,'*char')', ... + 'dim' , fread(pFile,8,'int16')', ... + 'intent_p1' , fread(pFile,1,'float32')', ... + 'intent_p2' , fread(pFile,1,'float32')', ... + 'intent_p3' , fread(pFile,1,'float32')', ... + 'intent_code' , fread(pFile,1,'int16')', ... + 'datatype' , fread(pFile,1,'int16')', ... + 'bitpix' , fread(pFile,1,'int16')', ... + 'slice_start' , fread(pFile,1,'int16')', ... + 'pixdim' , fread(pFile,8,'float32')', ... + 'vox_offset' , fread(pFile,1,'float32')', ... + 'scl_slope' , fread(pFile,1,'float32')', ... + 'scl_inter' , fread(pFile,1,'float32')', ... + 'slice_end' , fread(pFile,1,'int16')', ... + 'slice_code' , fread(pFile,1,'*char')', ... + 'xyzt_units' , fread(pFile,1,'*char')', ... + 'cal_max' , fread(pFile,1,'float32')', ... + 'cal_min' , fread(pFile,1,'float32')', ... + 'slice_duration' , fread(pFile,1,'float32')', ... + 'toffset' , fread(pFile,1,'float32')', ... + 'glmax' , fread(pFile,1,'int32')', ... + 'glmin' , fread(pFile,1,'int32')', ... + 'descrip' , (fread(pFile,80,'*char')'), ... + 'aux_file' , (fread(pFile,24,'*char')'), ... + 'qform_code' , fread(pFile,1,'int16')', ... + 'sform_code' , fread(pFile,1,'int16')', ... + 'quatern_b' , fread(pFile,1,'float32')', ... + 'quatern_c' , fread(pFile,1,'float32')', ... + 'quatern_d' , fread(pFile,1,'float32')', ... + 'qoffset_x' , fread(pFile,1,'float32')', ... + 'qoffset_y' , fread(pFile,1,'float32')', ... + 'qoffset_z' , fread(pFile,1,'float32')', ... + 'srow_x' , fread(pFile,4,'float32')', ... + 'srow_y' , fread(pFile,4,'float32')', ... + 'srow_z' , fread(pFile,4,'float32')', ... + 'intent_name' , (fread(pFile,16,'*char')'), ... + 'magic' , (fread(pFile,4,'*char')'), ... + 'originator' , fread(pFile, 5,'int16'),... + 'esize' , 0, ... + 'ecode' , 0, ... + 'edata' , '' ... +); +% this part is intended to read the extension data information at the end of the +% nifti header and before the image proper +%extendcode = fread(pFile,4,'char'); +%if extendcode(1)~=0 +% hdr.esize = hdr.vox_offset-352; +% hdr.edata = fread(pFile, hdr.esize, 'char'); +%end + +fclose(pFile); + + +return diff --git a/som/SOM_read_nii_img.m b/som/SOM_read_nii_img.m new file mode 100755 index 00000000..cbdf9bc3 --- /dev/null +++ b/som/SOM_read_nii_img.m @@ -0,0 +1,91 @@ +% +% Borrowed from Luis Hernandez +% +% Robert Welsh +% 2011 +% + + +function [d, hdr]= SOM_read_nii_img(name) +%function [d [,hdr] ] = read_nii_img(name) +[path root suffix] = fileparts(name); + +if strcmp(suffix,'.gz') + fprintf('\n Unzipping ...%s', name); + eval(['!gunzip ' name]); + hdr = SOM_read_nii_hdr(name); + +elseif strcmp(suffix,'.nii') + name = name; + hdr = SOM_read_nii_hdr(name); + +elseif strcmp(suffix,'.img') + [d hdr] = read_img(name); + hdr = avw2nii_hdr(hdr); + return +end + +[pFile,messg] = fopen(name, 'r','native'); +if pFile == -1 + fprintf('Error opening header file: %s',name); + fprintf('\n%s',messg); + return; +end + +endian = SOM_img_endian(name); +[pFile,messg] = fopen(name, 'r',endian); + +fseek(pFile, hdr.vox_offset, 'bof'); + +xdim = hdr.dim(2); +ydim = hdr.dim(3); +zdim = hdr.dim(4); +tdim = hdr.dim(5); + + +switch hdr.datatype + case 0 + fmt = 'uint8'; + case 2 + fmt = 'uint8'; + case 4 + fmt = 'short'; + case 8 + fmt = 'int'; + case 16 + fmt = 'float'; + case 32 + fmt = 'float'; + xdim = hdr.xdim * 2; + ydim = hdr.ydim * 2; + case 64 + fmt = 'int64'; + otherwise + errormesg(sprintf('Data Type %d Unsupported. Aborting',hdr.datatype)); + return + +end + + + +% Read in data. +d = (fread(pFile,[xdim*ydim*zdim*tdim], fmt))'; +if tdim >=2 + d = reshape(d, xdim*ydim*zdim, tdim); + d=d'; +else + d = reshape(d, [xdim ydim zdim]); +end +fclose(pFile); + +if strcmp(suffix,'.gz') + fprintf('\n Done reading file. Re-zipping ...%s\n', name); + eval(['!gzip ' root]); +end + +if nargout == 2 + varargout(1) = {hdr}; +end + +return + diff --git a/som/SOM_roiPointsInMask.m b/som/SOM_roiPointsInMask.m new file mode 100644 index 00000000..2601e44a --- /dev/null +++ b/som/SOM_roiPointsInMask.m @@ -0,0 +1,126 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% +% To determine if MNI coordinates are inside +% a binary mask image, looking at the sagital plane. +% +% +% function inOutMask = roiPointsInMask(PMask,ROICoords) +% +% PMask = masking image +% ROICoords = coordinates of the ROI in MNI to be checked. +% +% Return an array of indices that point to the coordinates in ROICoords +% that are within the mask. +% +% +% function inOutMask = SOM_roiPointsInMask(PMask,ROICoords) +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function inOutMask = SOM_roiPointsInMask(PMask,ROICoords) + +% Default is nothing in the mask. + +inOutMask = []; + +% Make sure that the coordinates are a n x 3 array + +if size(ROICoords,2) ~= 3 + SOM_LOG('FATAL ERROR : Coordinates need to be x,y,z'); + return +end + +inOutMask = zeros(size(ROICoords,1),1); + +% Make sure that the masking file exists and can be read. + +if exist(PMask,'file') == 0 + SOM_LOG('FATAL ERROR : Masking image is missing'); + return +end + +try + MVol = spm_read_vols(spm_vol(PMask)); +catch + SOM_LOG(sprintf('FATAL ERROR : spm can not read the masking file %s',PMask)); + return +end + +% Let's re-binerize the mask + +MVol = MVol > 0; + +% We need the header so that we can get into the reference frame of the +% image. + +MHdr = spm_vol(PMask); + +% Get the voxel numbers. +% +% By using the "mat" transformation matrix we can introduce a +% left/right flip. So you need to make sure that your mask and your +% time-series data are in the handedness. + +ROICoordsVoxel = round((inv(MHdr.mat)*([ROICoords ones(size(ROICoords,1),1)]'))'); + +% Make a volume and use the index numbers as the values in the volume and +% then mask that with the masking volume and then search for remaining +% values. These are then the ROI indices that will be in the mask. + +VALIDIDX = ones(length(ROICoordsVoxel(:,1)),1); + +% Any voxels that are outside image we set to zero. + +BADIDX1 = find(ROICoordsVoxel(:,1)>size(MVol,1)); +BADIDX2 = find(ROICoordsVoxel(:,2)>size(MVol,2)); +BADIDX3 = find(ROICoordsVoxel(:,3)>size(MVol,3)); + +ROICoordsVoxel(BADIDX1,1) = 0; +ROICoordsVoxel(BADIDX2,2) = 0; +ROICoordsVoxel(BADIDX3,3) = 0; + +BADIDX1 = find(ROICoordsVoxel(:,1)<1); +BADIDX2 = find(ROICoordsVoxel(:,2)<1); +BADIDX3 = find(ROICoordsVoxel(:,3)<1); + +ROICoordsVoxel(BADIDX1,1) = 0; +ROICoordsVoxel(BADIDX2,2) = 0; +ROICoordsVoxel(BADIDX3,3) = 0; + +% The good voxels will have a "1" while bad will be a zero. + +GOODCALC = ROICoordsVoxel(:,1).*ROICoordsVoxel(:,2).*ROICoordsVoxel(:,3); + +% Now find the indices of the ones that are good. + +IDXGood = find(GOODCALC); + +% Now find out where they would appear in the image. + +LINEARIDX = sub2ind(size(MVol),ROICoordsVoxel(IDXGood,1),ROICoordsVoxel(IDXGood,2),ROICoordsVoxel(IDXGood,3)); + +IDXVOL = 0*MVol; + +% Set the voxel that is within the bounds of the image to have the value of +% the index from the array ROICoords. + +IDXVOL(LINEARIDX) = IDXGood; + +% Now mask this image with the masking image. + +MaskIDX = IDXVOL.*MVol; + +% Those that survive will have a value of the index from ROICoords and thus +% will be in the image. + +inOutMask = MaskIDX(find(MaskIDX)); + +% +% All done. +% diff --git a/som/SOM_tVar.m b/som/SOM_tVar.m new file mode 100644 index 00000000..cb230cf3 --- /dev/null +++ b/som/SOM_tVar.m @@ -0,0 +1,78 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% A routine to calculate the temporal variance map of a time-series +% data set. +% +% This is useful for creating a mask on-the-fly for regressor of +% physio nuiscance. +% +% function results = SOM_tVar(NIFTI4DFILE,outputName) +% +% Input -- is times-series data, and masking image +% +% NIFTI4DFILE - name of ".nii" file to calculate variance. +% +% outputName - name of output variance map +% if absoluate path is given that will be +% used else it will be written in relative +% path to the NIFTI4DFILE +% +% Output -- +% +% results - name of output file written. A nifti file. +% +% +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_tVar(NIFTI4DFILE,outputName) + +results = -1; + +% Open the file, but catch any error. + +try + vol4D = nifti(NIFTI4DFILE); +catch + SOM_LOG(sprintf('WARNING : input file can not be opened : %s',NIFT4DFILE)); + return +end + +% Now calculate the variance map. + +% First some sanity checks. + +if length(vol4D.dat.dim) < 4 + SOM_LOG(sprintf('WARNING : Input file must be 4D! : %s',NIFT4DFILE)); + return +end + +if vol4D.dat.dim(4) < 2 + SOM_LOG(sprintf('WARNING : Input file must be 4D! : %s',NIFT4DFILE)); + return +end + +% Okay, we are good to go. + +t_VarMap = var(vol4D.dat(:,:,:,:),[],4); + +% Get the path of the 4D file. + +[fp fn fe] = fileparts(NIFTI4DFILE); + +if outputName(1) ~= '/' + results = fullfile(fp,outputName); +end + +[fpO fnO feO] = fileparts(outputName); + +% Now write it out. + +SOM_WriteNII(NIFTI4DFILE,results,t_VarMap,'FLOAT32-LE'); + +return diff --git a/som/SOM_tVarMask.m b/som/SOM_tVarMask.m new file mode 100644 index 00000000..5e63bca9 --- /dev/null +++ b/som/SOM_tVarMask.m @@ -0,0 +1,123 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2011 +% +% Ann Arbor, MI +% +% Routine to take temporal variance map and a mask +% and create a mask of the top P% flucuating areas. +% +% function results = SOM_tVarMask(varianceMap,brainMask,P,outputName) +% +% Input -- +% +% varianceMap - a variance map usually made from SOM_tVar +% or from "fslmaths [input] -Tstd [output] +% +% brainMask - a mask to get only brain - good to remove +% the eyes +% +% P - take the top "P" percent of flucuating +% voxels +% +% +% outputName - name of filet to create, either with absolute +% path of relative path. +% +% Output -- +% +% results - +% .outputName - output name +% .xBins - bins of histogram +% .hvarMap - histogram of values +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function results = SOM_tVarMask(varianceMap,brainMask,P,outputName) + +results = -1; + +% Open the variance map + +try + varMap = nifti(varianceMap); +catch + SOM_LOG(sprintf('WARNING : Variance map can not be opened %s',varianceMap)); + return +end + +% Open the masking file + +try + maskImg = nifti(brainMask); +catch + SOM_LOG(sprintf('WARNING : Masking image can not be opened %s',brainMask)); + return +end + +% Check for any difference + +if any(varMap.dat.dim(1:3) - maskImg.dat.dim(1:3)) + SOM_LOG('WARNING : Masking image and variance map do not match.'); + return +end + +% Check to see if length of P is adequate. + +if length(P) > 1 + SOM_LOG('P must be a scalar'); + return +end + +maskVarMap = varMap.dat(:,:,:).*maskImg.dat(:,:,:); +% Write out the results. + +% Get the path of the 4D file. + +[fp fn fe] = fileparts(varianceMap); + +if outputName(1) ~= '/' + outputName = fullfile(fp,outputName); +end + +% Calculate teh top P percent of the voxels. + +iMaskNonZero = find(maskVarMap>0); + +varVals = maskVarMap(iMaskNonZero); + +xBins = [0:.001:1]*max(varVals); + +hvarVals = hist(varVals,xBins); + +chvarVals = cumsum(hvarVals); +chvarVals = chvarVals/max(chvarVals); + +idxVals = find(chvarVals>=(1-P)); + +% Need some error checking? + +if length(idxVals) < 1 + SOM_LOG('ERROR : Can find any voxels -- weird, call Robert and debug'); + return +end + +ValThresh = xBins(idxVals(1)); + +% Now write it out. + +maskVarMap = maskVarMap >=ValThresh; + +SOM_WriteNII(varianceMap,outputName,maskVarMap,'FLOAT32-LE'); + +clear results + +results.outputName = outputName; +results.xBins = xBins; +results.hvarMap = hvarVals; +results.nVoxels = length(find(maskVarMap)); +results.nVoxelsPerZ = squeeze(sum(squeeze(sum(maskVarMap,1)),1)); +results.P = P; + +return diff --git a/som/SOM_texturizer/sphere_hist.m b/som/SOM_texturizer/sphere_hist.m new file mode 100755 index 00000000..d05e5eb6 --- /dev/null +++ b/som/SOM_texturizer/sphere_hist.m @@ -0,0 +1,58 @@ +nSpace = 40000; +%[b,v,t] = sphere_iter(3,1); +%i = []; +%for ii=1:40000:nSpace + %D1 = rand(400000,3)-.5; + %D1 = randn(40000,3); + %D1 = SOM_UnitNormMatrix(D1,2); + %udotv = D1*v'; + %y = sum(udotv>.992,1); + %[y,i_tmp] = sum(udotv,[],2); + %i = [i;i_tmp]; +%end +%y = hist(i,1:length(v)); +%closest = v*v2'; +%[y,i] = max(closest,[],2); +%y = colors(i)'; +%whos y +%y = zeros(length(v),1); +%y(1:length(v2)) = randn(length(v2),1); +%y = y'; +tri_v = zeros(length(t),4,3); +size([v(t(:,1),1),v(t(:,2),1),v(t(:,3),1),v(t(:,4),1)]) +tri_v(:,:,1) = [v(t(:,1),1),v(t(:,2),1),v(t(:,3),1),v(t(:,4),1)]; +tri_v(:,:,2) = [v(t(:,1),2),v(t(:,2),2),v(t(:,3),2),v(t(:,4),2)]; +tri_v(:,:,3) = [v(t(:,1),3),v(t(:,2),3),v(t(:,3),3),v(t(:,4),3)]; +col_v = zeros(length(t),4); +col_v(:,:) = [y(t(:,1))',y(t(:,1))',y(t(:,1))',y(t(:,1))']; +%axes('Box','on'); +subplot(2,2,1); +fill3(tri_v(:,:,1)',tri_v(:,:,2)',tri_v(:,:,3)',col_v(:,:,1)','FaceAlpha',1,'FaceLighting','phong','SpecularStrength',1); +axis off +axis equal +subplot(2,2,4); +fill3(tri_v(:,:,1)',tri_v(:,:,2)',tri_v(:,:,3)',col_v(:,:,1)','FaceAlpha',1,'FaceLighting','phong','SpecularStrength',1); +%axes('Visible','off'); +axis off +axis equal +subplot(2,2,2); +fill3(tri_v(:,:,1)',tri_v(:,:,2)',tri_v(:,:,3)',col_v(:,:,1)','FaceAlpha',1,'FaceLighting','phong','SpecularStrength',1); +%axes('Visible','off'); +axis off +axis equal +subplot(2,2,1); +v_ext = results_initial.v2*1.05; +camva(50); +for ii=1:size(results_initial.v2,1) + text(v_ext(ii,1),v_ext(ii,2),v_ext(ii,3),num2str(ii),'HorizontalAlignment','center','ButtonDownFcn',['subplot(2,2,1);campos([',num2str(v_ext(ii,1)*2),',',num2str(v_ext(ii,2)*2),',',num2str(v_ext(ii,3)*2),']);subplot(2,2,4);campos([',num2str(v_ext(ii,1)*2),',',num2str(v_ext(ii,2)*2),',',num2str(v_ext(ii,3)*2),']);subplot(2,2,3);plot(SelfOMap(:,',num2str(ii),'));subplot(2,2,2);campos([',num2str(-v_ext(ii,1)*2),',',num2str(-v_ext(ii,2)*2),',',num2str(-v_ext(ii,3)*2),']);'],'BackgroundColor',[1 1 1]) +end +campos([2 0 0]); +subplot(2,2,4); +camva(50); +text(v_ext(:,1),v_ext(:,2),v_ext(:,3),num2str([1:size(results_initial.v2,1)]'),'HorizontalAlignment','center') +campos([2 0 0]); +subplot(2,2,2); +camva(50); +campos([-2 0 0]); +text(v_ext(:,1),v_ext(:,2),v_ext(:,3),num2str([1:size(results_initial.v2,1)]'),'HorizontalAlignment','center') +drawnow diff --git a/som/SOM_texturizer/texturizer_v4.m b/som/SOM_texturizer/texturizer_v4.m new file mode 100755 index 00000000..a9b2a76e --- /dev/null +++ b/som/SOM_texturizer/texturizer_v4.m @@ -0,0 +1,96 @@ +function [b,v,t] = texturizer(v) + +v_x = reshape(v,[size(v,1),1,3]); +v_y = reshape(v,[1,size(v,1),3]); +v_x_rep = repmat(v_x,[1,size(v,1),1]); +v_y_rep = repmat(v_y,[size(v,1),1,1]); +dist = v_x_rep-v_y_rep; +dist = dist.^2; +dist = sqrt(squeeze(sum(dist,3))); +t = zeros(0,3); +b = zeros(size(v,1)); +new_triangles = []; +old_triangles = []; +tri_combs = nchoosek(1:3,2); +for ii=1:size(v,1) + ii + [y,i2] = sort(dist(ii,:)); + i2 = i2(2:end); + num_vertices = 3; + flag = false; + g = []; + %sphere_hist + %text(v(:,1),v(:,2),v(:,3),num2str([1:100]')) + %if ii==10 + % pause; + %end + new_triangles = []; + for num_vertices = 3:8 + tri_length = []; + vertices = find(b(ii,:)>0); + count = 1; + while length(vertices) < num_vertices + if any(vertices==i2(count)) + count = count + 1; + else + vertices = [vertices,i2(count)]; + count = count + 1; + end + end + combs = nchoosek(vertices,2); + for jj=1:size(combs,1) + tri_length(jj) = dist(combs(jj,1),combs(jj,2));%dist(ii,combs(jj,1))+dist(ii,combs(jj,2))+dist(combs(jj,1),combs(jj,2)); + end + [y,i] = sort(tri_length); + for num_triangles = 1:length(vertices) + y = hist([combs(i(1:num_triangles),1);combs(i(1:num_triangles),2)],[1:size(v,1)]); + if any(find(y>2)) + i = [i(1:num_triangles-1),i(num_triangles+1:end)]; + end + end + for jj=1:size(combs,1) + tri_length(jj) = dist(ii,combs(jj,1))+dist(ii,combs(jj,2))+dist(combs(jj,1),combs(jj,2)); + end + g = [g mean(tri_length(i(1:length(vertices))))]; + new_triangles{num_vertices} = sort([combs(i(1:length(vertices)),:),repmat(ii,[length(vertices),1])],2); + end + [y,i] = sort(g); + new_triangles = new_triangles{i(1)+2}; + for jj=1:size(new_triangles,1) + temp_compare = sum(t==repmat(new_triangles(jj,:),[size(t,1),1]),2); + cur_b = [b(new_triangles(jj,1),new_triangles(jj,2)); + b(new_triangles(jj,1),new_triangles(jj,3)); + b(new_triangles(jj,2),new_triangles(jj,3))]; + if all(temp_compare~=3) & ~any(cur_b>=2) + t = [t;new_triangles(jj,:)]; + b(new_triangles(jj,1),new_triangles(jj,2)) = b(new_triangles(jj,1),new_triangles(jj,2))+1; + b(new_triangles(jj,1),new_triangles(jj,3)) = b(new_triangles(jj,1),new_triangles(jj,3))+1; + b(new_triangles(jj,2),new_triangles(jj,3)) = b(new_triangles(jj,2),new_triangles(jj,3))+1; + b(new_triangles(jj,2),new_triangles(jj,1)) = b(new_triangles(jj,2),new_triangles(jj,1))+1; + b(new_triangles(jj,3),new_triangles(jj,1)) = b(new_triangles(jj,3),new_triangles(jj,1))+1; + b(new_triangles(jj,3),new_triangles(jj,2)) = b(new_triangles(jj,3),new_triangles(jj,2))+1; + end + end +end +f_t = []; +p = []; +[x,y] = ind2sub(size(b),find(b)); +for ii=1:length(x) + v = [v;(v(x(ii),:)+v(y(ii),:))/2]; + b(x(ii),y(ii)) = length(v); + b(y(ii),x(ii)) = length(v); +end +min_t = Inf; +for ii=1:length(t) + new_point = (v(t(ii,1),:)+v(t(ii,2),:)+v(t(ii,3),:))/3; + v = [v;new_point]; + for jj=1:3 + %b(t(ii,jj),t(ii,mod(jj+3,3)+1))% < min_t + % min_t = t(ii,jj); + %end + f_t = [f_t;t(ii,jj),b(t(ii,jj),t(ii,mod(jj+1,3)+1)),length(v),b(t(ii,jj),t(ii,mod(jj+3,3)+1))]; + end +end +min_t +t = f_t; +b(find(b)) = 1; \ No newline at end of file diff --git a/som/autoCorrelatePlane.m b/som/autoCorrelatePlane.m new file mode 100644 index 00000000..abf3c922 --- /dev/null +++ b/som/autoCorrelatePlane.m @@ -0,0 +1,85 @@ +% +% a function to calculate the autocorrelation of a grey matter +% voxel with its neighbors +% +% function results = autoCorrelateVol(theSlice,option) +% +% option = 'FULL' if you want to examine the +% full cube about the middle. +% + +function results = autoCorrelatePlane(theSlice,varargin) + +theDIM = size(theSlice); + +tmpVol = zeros(theDIM+[6 6]); + +tmpVol(4:end-3,4:end-3) = theSlice; + +results = zeros(theDIM); + +theShifts = [ + +0 +0 +0 ;... + -1 +0 +0 ; ... + +1 +0 +0 ; ... + +0 -1 0 ; ... + +0 +1 0 ; ... + +0 0 -1 ; ... + +0 0 +1 ; ... + -1 -1 0 ; ... + +1 -1 0 ; ... + -1 +1 0 ; ... + +1 +1 0 ; ... + -1 0 -1 ; ... + +1 0 -1 ; ... + -1 0 +1 ; ... + +1 0 +1 ; ... + +0 -1 -1 ; ... + +0 +1 -1 ; ... + +0 -1 +1 ; ... + +0 +1 +1 ]; + +if nargin > 1 + if strcmp(upper(varargin{1}),'FULL') + + theShifts = []; + for ix = -1:1 + for iy = -1:1 + theShifts = [theShifts; ix iy]; + end + end + elseif strcmp(upper(varargin{1}),'FULL5') + + theShifts = []; + for ix = -2:2 + for iy = -2:2 + theShifts = [theShifts; ix iy]; + end + end + elseif strcmp(upper(varargin{1}),'FULL7') + + theShifts = []; + for ix = -3:3 + for iy = -3:3 + theShifts = [theShifts; ix iy]; + end + end + end +end + + +for iShift = 1:size(theShifts,1) + if strcmp(upper(varargin{1}),'FULL5') + theCorner = theShifts(iShift,:) + 3; + elseif strcmp(upper(varargin{1}),'FULL7') + theCorner = theShifts(iShift,:) + 4; + else + theCorner = theShifts(iShift,:) + 2; + end + theEnd = theCorner + theDIM - 1; + results = results + theSlice.*tmpVol(theCorner(1):theEnd(1),theCorner(2):theEnd(2)); +end + +% +% all done. +% diff --git a/som/autoCorrelateVol.m b/som/autoCorrelateVol.m new file mode 100755 index 00000000..25c85ad0 --- /dev/null +++ b/som/autoCorrelateVol.m @@ -0,0 +1,91 @@ +% +% a function to calculate the autocorrelation of a grey matter +% voxel with its neighbors +% +% function results = autoCorrelateVol(theVol,option) +% +% option = 'FULL' if you want to examine the +% full cube about the middle. +% + +function results = autoCorrelateVol(theVol,varargin) + +theDIM = size(theVol); + +tmpVol = zeros(theDIM+[6 6 6]); + +tmpVol(4:end-3,4:end-3,4:end-3) = theVol; + +results = zeros(theDIM); + +theShifts = [ + +0 +0 +0 ;... + -1 +0 +0 ; ... + +1 +0 +0 ; ... + +0 -1 0 ; ... + +0 +1 0 ; ... + +0 0 -1 ; ... + +0 0 +1 ; ... + -1 -1 0 ; ... + +1 -1 0 ; ... + -1 +1 0 ; ... + +1 +1 0 ; ... + -1 0 -1 ; ... + +1 0 -1 ; ... + -1 0 +1 ; ... + +1 0 +1 ; ... + +0 -1 -1 ; ... + +0 +1 -1 ; ... + +0 -1 +1 ; ... + +0 +1 +1 ]; + +if nargin > 1 + if strcmp(upper(varargin{1}),'FULL') + + theShifts = []; + for iz = -1:1 + for iy = -1:1 + for ix = -1:1 + theShifts = [theShifts; ix iy iz]; + end + end + end + elseif strcmp(upper(varargin{1}),'FULL5') + + theShifts = []; + for iz = -2:2 + for iy = -2:2 + for ix = -2:2 + theShifts = [theShifts; ix iy iz]; + end + end + end + elseif strcmp(upper(varargin{1}),'FULL7') + + theShifts = []; + for iz = -3:3 + for iy = -3:3 + for ix = -3:3 + theShifts = [theShifts; ix iy iz]; + end + end + end + end +end + + +for iShift = 1:size(theShifts,1) + if strcmp(upper(varargin{1}),'FULL5') + theCorner = theShifts(iShift,:) + 3; + elseif strcmp(upper(varargin{1}),'FULL7') + theCorner = theShifts(iShift,:) + 4; + else + theCorner = theShifts(iShift,:) + 2; + end + theEnd = theCorner + theDIM - 1; + results = results + theVol.*tmpVol(theCorner(1):theEnd(1),theCorner(2):theEnd(2),theCorner(3):theEnd(3)); +end + +% +% all done. +% diff --git a/som/diffCode.sh b/som/diffCode.sh new file mode 100755 index 00000000..d210f7de --- /dev/null +++ b/som/diffCode.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +THISDIR=`pwd` +THATDIR=/Volumes/ALS/Software/SOMWork/SOMBen/SOM + +if [ ! -z "$1" ] +then + THATDIR=$1 +fi + +echo +echo Comparing stuff in $THISDIR to things in $THATDIR +echo +cd $THISDIR +for FILE in `ls SOM*.m` ; do echo " * * * * * * $FILE * * * * * " ; diff $FILE $THATDIR ; done + +echo +echo Comparing stuff in $THATDIR to things in $THISDIR +echo +cd $THATDIR +for FILE in `ls SOM*.m` ; do echo " * * * * * * $FILE * * * * * " ; diff $FILE $THISDIR; done + +cd $THISDIR + diff --git a/som/draw_rois.m b/som/draw_rois.m new file mode 100644 index 00000000..95114e27 --- /dev/null +++ b/som/draw_rois.m @@ -0,0 +1,38 @@ +% +% add path to /Users/rcwelsh/matlabScripts/DrawObjects +% .....SOM_V.../ +% +nVoxels = []; +nRad = 0; +clear XYZ +XYZ = {}; + +radiusVoxels = []; +for iRad = 0:.05:5 + nRad = nRad + 1; + XYZ{nRad}=SOM_MakeSphereROI(iRad) + nVoxels=[nVoxels size(XYZ{nRad},2)] + radiusVoxels = [radiusVoxels iRad]; +end + +unVoxels = unique(nVoxels); + +for iVox = 1:length(unVoxels) + idxRad = find(nVoxels == unVoxels(iVox)); + idxRad = idxRad(1); + figure(iVox); + cubes = {}; + for iCube = 1:size(XYZ{idxRad},2) + cubes{iCube} = DO_Translate(cube1s,XYZ{idxRad}(1,iCube),XYZ{idxRad}(2,iCube),XYZ{idxRad}(3,iCube)); + end + hold on; + for iCube = 1:length(cubes) + cubes{iCube}.edgecolor = [0 0 0]; + DO_Renderpatch(cubes{iCube}); + end + axis off + axis square + view(35,45); + title(sprintf('n Voxels : %d, Radius = %f',size(XYZ{idxRad},2),radiusVoxels(idxRad)),'fontweight','bold','fontsize',14); + print('-dpng',sprintf('roi_n_%03d_%2.2f_radius.png',size(XYZ{idxRad},2),radiusVoxels(idxRad))); +end diff --git a/som/nVoxelsPerRIO.pdf b/som/nVoxelsPerRIO.pdf new file mode 100644 index 00000000..ef42d947 Binary files /dev/null and b/som/nVoxelsPerRIO.pdf differ diff --git a/som/nVoxelsPerRIO.png b/som/nVoxelsPerRIO.png new file mode 100644 index 00000000..dfd06636 Binary files /dev/null and b/som/nVoxelsPerRIO.png differ diff --git a/som/roi_n_001_0.00_radius.png b/som/roi_n_001_0.00_radius.png new file mode 100644 index 00000000..1b075df4 Binary files /dev/null and b/som/roi_n_001_0.00_radius.png differ diff --git a/som/roi_n_007_1.00_radius.png b/som/roi_n_007_1.00_radius.png new file mode 100644 index 00000000..11c570cd Binary files /dev/null and b/som/roi_n_007_1.00_radius.png differ diff --git a/som/roi_n_019_1.45_radius.png b/som/roi_n_019_1.45_radius.png new file mode 100644 index 00000000..eabb14c9 Binary files /dev/null and b/som/roi_n_019_1.45_radius.png differ diff --git a/som/roi_n_027_1.75_radius.png b/som/roi_n_027_1.75_radius.png new file mode 100644 index 00000000..e1aee5e2 Binary files /dev/null and b/som/roi_n_027_1.75_radius.png differ diff --git a/som/roi_n_033_2.00_radius.png b/som/roi_n_033_2.00_radius.png new file mode 100644 index 00000000..3a0b08f7 Binary files /dev/null and b/som/roi_n_033_2.00_radius.png differ diff --git a/som/roi_n_057_2.25_radius.png b/som/roi_n_057_2.25_radius.png new file mode 100644 index 00000000..7a3b96f8 Binary files /dev/null and b/som/roi_n_057_2.25_radius.png differ diff --git a/som/roi_n_081_2.45_radius.png b/som/roi_n_081_2.45_radius.png new file mode 100644 index 00000000..75931746 Binary files /dev/null and b/som/roi_n_081_2.45_radius.png differ diff --git a/som/roi_n_093_2.85_radius.png b/som/roi_n_093_2.85_radius.png new file mode 100644 index 00000000..9a67a647 Binary files /dev/null and b/som/roi_n_093_2.85_radius.png differ diff --git a/som/roi_n_123_3.00_radius.png b/som/roi_n_123_3.00_radius.png new file mode 100644 index 00000000..8bf7a44c Binary files /dev/null and b/som/roi_n_123_3.00_radius.png differ diff --git a/som/roi_n_147_3.20_radius.png b/som/roi_n_147_3.20_radius.png new file mode 100644 index 00000000..29dc5413 Binary files /dev/null and b/som/roi_n_147_3.20_radius.png differ diff --git a/som/roi_n_171_3.35_radius.png b/som/roi_n_171_3.35_radius.png new file mode 100644 index 00000000..dbd48f53 Binary files /dev/null and b/som/roi_n_171_3.35_radius.png differ diff --git a/som/roi_n_179_3.50_radius.png b/som/roi_n_179_3.50_radius.png new file mode 100644 index 00000000..0473a5e2 Binary files /dev/null and b/som/roi_n_179_3.50_radius.png differ diff --git a/som/roi_n_203_3.65_radius.png b/som/roi_n_203_3.65_radius.png new file mode 100644 index 00000000..2ec99238 Binary files /dev/null and b/som/roi_n_203_3.65_radius.png differ diff --git a/som/roi_n_251_3.75_radius.png b/som/roi_n_251_3.75_radius.png new file mode 100644 index 00000000..37967e57 Binary files /dev/null and b/som/roi_n_251_3.75_radius.png differ diff --git a/som/roi_n_257_4.00_radius.png b/som/roi_n_257_4.00_radius.png new file mode 100644 index 00000000..2e5808fe Binary files /dev/null and b/som/roi_n_257_4.00_radius.png differ diff --git a/som/roi_n_305_4.15_radius.png b/som/roi_n_305_4.15_radius.png new file mode 100644 index 00000000..036a7899 Binary files /dev/null and b/som/roi_n_305_4.15_radius.png differ diff --git a/som/roi_n_341_4.25_radius.png b/som/roi_n_341_4.25_radius.png new file mode 100644 index 00000000..8e814a8d Binary files /dev/null and b/som/roi_n_341_4.25_radius.png differ diff --git a/som/roi_n_365_4.40_radius.png b/som/roi_n_365_4.40_radius.png new file mode 100644 index 00000000..eda396cd Binary files /dev/null and b/som/roi_n_365_4.40_radius.png differ diff --git a/som/roi_n_389_4.50_radius.png b/som/roi_n_389_4.50_radius.png new file mode 100644 index 00000000..03d70021 Binary files /dev/null and b/som/roi_n_389_4.50_radius.png differ diff --git a/som/roi_n_437_4.60_radius.png b/som/roi_n_437_4.60_radius.png new file mode 100644 index 00000000..9ce81092 Binary files /dev/null and b/som/roi_n_437_4.60_radius.png differ diff --git a/som/roi_n_461_4.70_radius.png b/som/roi_n_461_4.70_radius.png new file mode 100644 index 00000000..c5e4f5f1 Binary files /dev/null and b/som/roi_n_461_4.70_radius.png differ diff --git a/som/roi_n_485_4.90_radius.png b/som/roi_n_485_4.90_radius.png new file mode 100644 index 00000000..f462d0ef Binary files /dev/null and b/som/roi_n_485_4.90_radius.png differ diff --git a/som/roi_n_515_5.00_radius.png b/som/roi_n_515_5.00_radius.png new file mode 100644 index 00000000..a7a17da1 Binary files /dev/null and b/som/roi_n_515_5.00_radius.png differ diff --git a/som/show.m b/som/show.m new file mode 100755 index 00000000..8d2785b9 --- /dev/null +++ b/som/show.m @@ -0,0 +1,26 @@ +function [out, newim] = show(a,windfact) +% usage .. show(a,f); +% displays matrix "a" as a greyscale image in the matlab window +% and "f" is the optional window factors [min,max] + +if exist('windfact') == 0, + amin = min(min(a)); + amax = max(max(a)); + minmax = [amin,amax]; + a = (a - amin); + disp(['min/max= ',num2str(minmax(1)),' / ',num2str(minmax(2))]); +else + amin = windfact(1); + amax = windfact(2); + a = (a - amin); + a = a .* (a > 0); +end + +colormap(gray); +out=image((a)./(amax-amin).*64); +newim=(a)./(amax-amin).*64; +sfact=1/64*(amax-amin); +axis('image'); +axis('on'); +% grid on; + diff --git a/som/som_batch_mc_central.m b/som/som_batch_mc_central.m new file mode 100644 index 00000000..7a392389 --- /dev/null +++ b/som/som_batch_mc_central.m @@ -0,0 +1,214 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% General calculations that apply to both Preprocessing and First Level +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if (alreadydone(1)) + basefile = [stp basefile]; +end +if (alreadydone(2)) + basefile = [rep basefile]; +end +if (alreadydone(3)) + basefile = [nop basefile]; +end +if (alreadydone(4)) + basefile = [smp basefile]; +end + + +RunMode = [0 0]; +if (strcmpi(Mode,'full')) + RunMode = [1 1]; +else + RunMode(1) = strcmpi(Mode,'parameters'); + RunMode(2) = strcmpi(Mode,'som'); +end + +spm('defaults','fmri'); +global defaults +warning off all + +spmver = spm('Ver'); +if (strcmp(spmver,'SPM8')==1) + spm_jobman('initcfg'); + spm_get_defaults('cmdline',true); +end + +if (RunMode(1) | sum(RunMode) == 0) + RunNamesTotal = RunDir; + NumScanTotal = NumScan; + + MaskBrain = 1; + + RegressGlobal = any(strfind(upper(RegressOrder),'G')); + RegressWhite = any(strfind(upper(RegressOrder),'W')); + RegressCSF = any(strfind(upper(RegressOrder),'C')); + DoBandpassFilter = any(strfind(upper(RegressOrder),'B')); + RegressMotion = any(strfind(upper(RegressOrder),'M')); + DoLinearDetrend = any(strfind(upper(RegressOrder),'D')); + + for iSubject = 1:size(SubjDir,1) + clear parameters; + clear global SOM; + + parameters.RegressFLAGS.prinComp = PrincipalComponents; + parameters.RegressFLAGS.global = RegressGlobal; + parameters.RegressFLAGS.csf = RegressCSF; + parameters.RegressFLAGS.white = RegressWhite; + parameters.RegressFLAGS.motion = RegressMotion; + parameters.RegressFLAGS.order = RegressOrder; + + Subject=SubjDir{iSubject,1}; + RunList=SubjDir{iSubject,3}; + + NumRun = size(RunList,2); + + TotalNumRun = size(NumScanTotal,2); %%% number of image runs if every run were present + + %%%%% This code cuts RunDir and NumScan based which Image Runs are present + NumScan=[]; + clear RunDir; + for iRun=1:NumRun + RunDir{iRun,1}=RunNamesTotal{RunList(1,iRun)}; + NumScan=horzcat(NumScan,NumScanTotal(1,RunList(1,iRun))); + end + + NumRun= size(NumScan,2); % number of runs + ImageNumRun=size(RunDir,1); %number of image folders + + GreyPath = mc_GenPath(GreyMatterTemplate); + WhitePath = mc_GenPath(WhiteMatterTemplate); + CSFPath = mc_GenPath(CSFTemplate); + BrainPath = mc_GenPath(BrainMaskTemplate); + + parameters.grey.File = GreyPath; + parameters.grey.ImgThreshold = GreyThreshold; + parameters.masks.white.File = WhitePath; + parameters.masks.csf.File = CSFPath; + parameters.masks.epi.File = BrainPath; + parameters.rois.mask.MaskFLAG = MaskBrain; + + for iRun = 1:ImageNumRun + Run = RunDir{iRun}; + + ImagePath = mc_GenPath(ImageTemplate); + ImageFiles = spm_select('FPList',ImagePath, ['^' basefile '.*.' imagetype]); + RealignmentParametersFile = mc_GenPath(RealignmentParametersTemplate); + + parameters.data.run(iRun).P = ImageFiles; + + RealignmentParameters = load(RealignmentParametersFile); + RealignmentParametersDeriv = diff(RealignmentParameters); + RealignmentParametersDerivR = resample(RealignmentParametersDeriv,size(RealignmentParameters,1),size(RealignmentParametersDeriv,1)); + + parameters.data.run(iRun).MotionParameters = [RealignmentParameters RealignmentParametersDerivR]; + parameters.data.run(iRun).nTIME = NumScan(iRun); + parameters.data.MaskFLAG = MaskBrain; + + parameters.TIME.run(iRun).TR = TR; + parameters.TIME.run(iRun).BandFLAG = DoBandpassFilter; + parameters.TIME.run(iRun).TrendFLAG = DoLinearDetrend; + parameters.TIME.run(iRun).LowF = LowFrequency; + parameters.TIME.run(iRun).HiF = HighFrequency; + parameters.TIME.run(iRun).gentle = Gentle; + parameters.TIME.run(iRun).padding = Padding; + parameters.TIME.run(iRun).whichFilter = BandpassFilter; + parameters.TIME.run(iRun).fraction = Fraction; + end + + Run = RunDir{1}; + ImagePath = mc_GenPath(ImageTemplate); + SOM_Mask = mc_GenPath(fullfile(ImagePath,'som_mask.img')); + + if (isempty(BrainMaskTemplate)) + parameters.rois.mask.File = SOM_Mask; + else + parameters.rois.mask.File = BrainPath; + end + output.Template = OutputTemplate; + output.type = 1; + output.mode = 'makedir'; + if (RunMode(1)) + OutputPath = mc_GenPath(output); + else + OutputPath = mc_GenPath(OutputTemplate); + end + + switch (ROIInput) + case 'files' + ROIFolder = mc_GenPath(ROITemplate); + for iROIs = 1:size(ROIImages,1) + ROI{iROIs} = fullfile(ROIFolder,ROIImages{iROIs}); + end + parameters.rois.files = char(ROI); + case 'directory' + ROIFolder = mc_GenPath(ROITemplate); + parameters.rois.files = spm_select('FPList',ROIFolder,'.*\.img|.*\.nii'); + case 'coordinates' + parameters.rois.mni.coordinates = ROICenters; + if (iscell(ROISize)) + parameters.rois.mni.size = ROISize{1}; + else + XYZ = SOM_MakeSphereROI(ROISize); + parameters.rois.mni.size.XROI = XYZ(1,:); + parameters.rois.mni.size.YROI = XYZ(2,:); + parameters.rois.mni.size.ZROI = XYZ(3,:); + end + case 'grid' + ROIGridMask = mc_GenPath(ROIGridMaskTemplate); + ROIGridMaskHdr = spm_vol(ROIGridMask); + ROIGridBB = mc_GetBoundingBox(ROIGridMaskHdr); + grid_coord_cand = SOM_MakeGrid(ROIGridSpacing,ROIGridBB); + inOutIDX = SOM_roiPointsInMask(ROIGridMask,grid_coord_cand); + grid_coord = grid_coord_cand(inOutIDX,:); + parameters.rois.mni.coordinates = grid_coord; + if (iscell(ROIGridSize)) + parameters.rois.mni.size = ROIGridSize{1}; + else + XYZ = SOM_MakeSphereROI(ROIGridSize); + parameters.rois.mni.size.XROI = XYZ(1,:); + parameters.rois.mni.size.YROI = XYZ(2,:); + parameters.rois.mni.size.ZROI = XYZ(2,:); + end + end + + parameters.Output.correlation = ROIOutput; + parameters.Output.saveroiTC = saveroiTC; + %parameters.Output.description = 'description of output'; + parameters.Output.directory = OutputPath; + parameters.Output.name = OutputName; + ParameterFilename = [OutputName '_parameters']; + ParameterPath = mc_GenPath(fullfile(OutputPath,ParameterFilename)); + save(ParameterPath,'parameters'); + + end +end + +if (RunMode(2)) + for iSubject = 1:size(SubjDir,1) + clear D0 parameters results; + Subject=SubjDir{iSubject,1}; + %load existing parameter file + OutputPath = mc_GenPath(OutputTemplate); + load(fullfile(OutputPath,ParameterFilename)); + clear global SOM; + global SOM; + SOM.silent = 1; + SOM_LOG('STATUS : 01'); + + [D0 parameters] = SOM_PreProcessData(parameters); + if D0 == -1 + SOM_LOG('FATAL ERROR : No data returned'); + mc_Error('There is something wrong with your template or your data.\nNo data was returned from SOM_PreProcessData\n'); + else + results = SOM_CalculateCorrelations(D0,parameters); + if isnumeric(results) + SOM_LOG('FATAL ERROR : '); + mc_Error('There is something wrong with your template or your data.\nNo results were returned from SOM_CalculateCorrelations\n'); + end + end + end +end + + + + diff --git a/som/som_batch_mc_template.m b/som/som_batch_mc_template.m new file mode 100644 index 00000000..3b33d8ba --- /dev/null +++ b/som/som_batch_mc_template.m @@ -0,0 +1,329 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% GENERAL OPTIONS +%%% These options are shared among many of our scripts +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The folder that contains your subject folders +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Exp = '/net/data4/MAS/'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Path where your images are located +%%% +%%% Variables you can use in your template are: +%%% Exp = path to your experiment directory +%%% iSubject = index for subject +%%% Subject = name of subject from SubjDir (using iSubject as index of row) +%%% iRun = index of run (listed in Column 3 of SubjDir) +%%% Run = name of run from RunDir (using iRun as index of row) +%%% * = wildcard (can only be placed in final part of template) +%%% Examples: +%%% ImageTemplate = '[Exp]/Subjects/[Subject]/func/run_0[iRun]/'; +%%% ImageTemplate = '[Exp]/Subjects/[Subject]/TASK/func/[Run]/' +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ImageTemplate = '[Exp]/Subjects/[Subject]/TASK/func/[Run]/'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% A list of run folders where the script can find functional images +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RunDir = { + 'run_01/'; +}; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The list of subjects to process +%%% The format is 'subjectfolder',subject number in masterfile,[runs to include] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SubjDir = { +'5001/Tx1',50011,[1]; +'5002/Tx1',50021,[1]; +'5003/Tx1',50031,[1]; +'5004/Tx1',50041,[1]; +'5005/Tx1',50051,[1]; +'5010/Tx1',50101,[1]; +'5012/Tx1',50121,[1]; +'5014/Tx1',50141,[1]; +'5015/Tx1',50151,[1]; +'5016/Tx1',50161,[1]; +'5017/Tx1',50171,[1]; +'5018/Tx1',50181,[1]; +'5019/Tx1',50191,[1]; +'5020/Tx1',50201,[1]; +'5021/Tx1',50211,[1]; +'5023/Tx1',50231,[1]; +'5024/Tx1',50241,[1]; +'5025/Tx1',50251,[1]; +'5026/Tx1',50261,[1]; +'5028/Tx1',50281,[1]; +'5029/Tx1',50291,[1]; +'5031/Tx1',50311,[1]; +'5032/Tx1',50321,[1]; +'5034/Tx1',50341,[1]; +'5035/Tx1',50351,[1]; +'5036/Tx1',50361,[1]; +'5037/Tx1',50371,[1]; +'5038/Tx1',50381,[1]; +'5039/Tx1',50391,[1]; +'5040/Tx1',50401,[1]; +'5041/Tx1',50411,[1]; +'5042/Tx1',50421,[1]; + +'5001/Tx2',50012,[1]; +'5002/Tx2',50022,[1]; +'5003/Tx2',50032,[1]; +'5004/Tx2',50042,[1]; +'5005/Tx2',50052,[1]; +'5010/Tx2',50102,[1]; +'5012/Tx2',50122,[1]; +'5014/Tx2',50142,[1]; +'5015/Tx2',50152,[1]; +'5016/Tx2',50162,[1]; +'5017/Tx2',50172,[1]; +'5018/Tx2',50182,[1]; +'5019/Tx2',50192,[1]; +'5020/Tx2',50202,[1]; +'5021/Tx2',50212,[1]; +'5023/Tx2',50232,[1]; +'5024/Tx2',50242,[1]; +'5025/Tx2',50252,[1]; +'5026/Tx2',50262,[1]; +'5028/Tx2',50282,[1]; +'5029/Tx2',50292,[1]; +'5031/Tx2',50312,[1]; +'5032/Tx2',50322,[1]; +'5034/Tx2',50342,[1]; +'5035/Tx2',50352,[1]; +'5036/Tx2',50362,[1]; +'5037/Tx2',50372,[1]; +'5038/Tx2',50382,[1]; +'5039/Tx2',50392,[1]; +'5040/Tx2',50402,[1]; +'5041/Tx2',50412,[1]; +'5042/Tx2',50422,[1]; + }; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The TR your data was collected at +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +TR = 2; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Prefixes for slicetiming, realignment, normalization, and smoothing +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +stp = 'a'; +rep = 'r'; +nop = 'w'; +smp = 's'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Preprocessing that has already been completed on images +%%% [slicetime realign normalize smooth] +%%% If you are only running First Level (i.e. Preprocessing is already done) +%%% setting these will add the appropriate prefix to the basefile +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +alreadydone = [1 1 1 1]; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% The prefix of each functional file +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +basefile = 'restrun'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Image Type should be either 'nii' or 'img' +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +imagetype = 'nii'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Number of Functional scans per run +%%% (if you have more than 1 run, there should be more than 1 value here) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +NumScan = [180]; + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% CONNECTIVITY OPTIONS +%%% These options are only used for Connectivity +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Mode to run som_batch_mc_central in +%%% 'test' = test script but do not save parameters or run any +%%% SOM code +%%% 'parameters' = run script and save parameters for each subject +%%% but do not run any SOM code +%%% 'som' = run SOM code on previously saved parameters +%%% 'full' = generate parameters and immediately run SOM code +%%% +%%% NOTE: If you choose mode 'som' then most variables except +%%% SubjDir and OutputTemplate/OutputName will be ignored as they +%%% will be loaded from the already existing parameter file for each +%%% subject. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Mode = 'full'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Paths to your anatomical images +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +GreyMatterTemplate = '[Exp]/Subjects/[Subject]/anatomy/rgrey.img'; +WhiteMatterTemplate = '[Exp]/Subjects/[Subject]/anatomy/wm_mask.nii'; +CSFTemplate = '[Exp]/Subjects/[Subject]/anatomy/csf_mask.nii'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Where to output the data +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +OutputTemplate = '[Exp]/FirstLevel/[Subject]/[OutputName]/'; +OutputName = 'Striatum'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Path and name of explicit mask to use at first level. +%%% Leaving this blank ('') will use a subject-specific mask +%%% NOTE: Subject-specific masks are not recommended for grid usage below. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +BrainMaskTemplate = '[Exp]/ROIS/rEPI_MASK_NOEYES.img'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Path Template for realignment parameters file +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RealignmentParametersTemplate = '[Exp]/Subjects/[Subject]/TASK/func/[Run]/rp_arestrun_*'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Constrain results to only regions in GreyMatterTemplate (1=yes, 0=no) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +MaskGrey = 0; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Value threshold to use for each mask. If left as [] use default 0.75 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +GreyThreshold = []; +WhiteThreshold = []; +CSFThreshold = []; +EPIThreshold = []; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% the order to perform the regressions etc +%%% D = detrend +%%% G = global +%%% W = white matter +%%% C = csf +%%% M = motion +%%% B = bandpass +%%% +%%% Suggested order is "D[G]CWMB" +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RegressOrder = 'DCWMB'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Use this many principle components for regression +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +PrincipalComponents = 5; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Bandpass Filter Settings +%%% LowFrequency - low frequency cutoff +%%% HighFrequency - high frequency cutoff +%%% Gentle - 0 = no rolling, 1 = rolling +%%% Padding - number of timepoints to pad on beginning/end +%%% BandpassFilter - 0 = Matlab filter, 1 = SOM_Filter_FFT +%%% Fraction - fraction of variance for principle components +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +LowFrequency = 0.01; +HighFrequency = 0.1; +Gentle = 1; +Padding = 10; +BandpassFilter = 1; +Fraction = 1; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Type of input +%%% coordinates - provide the center of each seed and a radius +%%% files - provide a list of ROI files +%%% directory - provide a directory containing ROI files and the +%%% script will load all images in that directory to +%%% use as ROIs +%%% grid - make a grid based on provided spacing and masked +%%% by provided mask +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROIInput = 'directory'; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% If specifying ROI coordinates you need to provide a list of centers in +%%% MNI coordinates (mm) and a radius in voxels. +%%% NOTE: ROISize will be used as the radius of a sphere at each point. If +%%% you'd prefer to use the predefined 1,7,19, or 27 voxel sizes you will +%%% need to specify the size as a cell (i.e. {19}) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROICenters = [ + %0 -53 26; %pcc seed + 9 9 -8; %VSi right + -9 9 -8; %VSi left + 10 15 0; %VSs right + -10 15 0; %VSs left + 13 15 9; %DC right + -13 15 9; %DC left + 28 1 3; %DCP right + -28 1 3; %DCP left + 25 8 6; %DRP right + -25 8 6; %DRP left + 20 12 -3; %VRP right + -20 12 -3; %VRP left + ]; +ROISize = {19}; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% If specifying ROI images you need to provide an ROI folder as well as a +%%% cell array list of ROI images. If specifying an ROI directory, you only +%%% need to specify an ROITemplate. The script will then load all images +%%% in that directory to use as the ROIImages cell array. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROITemplate = '[Exp]/ROIS'; +ROIImages = { + 'image1.nii'; + 'image2.nii'; + }; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% If specifying ROI grid you need to provide a spacing and ROI size as +%%% well as an optional mask for grid point inclusion (a mask is strongly +%%% encouraged as not using one will return coordinates from across the entire +%%% bounding box). +%%% NOTE: ROIGridSize will be used as the radius of a sphere at each grid +%%% point. If you'd prefer to use the predefined 1,7,19, or 27 voxel sizes +%%% you will need to specify the size as a cell (i.e. {19}) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROIGridSpacing = 12; +ROIGridSize = {19}; +ROIGridMaskTemplate = '[Exp]/ROIS/ravg_gm_mask_and_EPI_mask.img'; +%ROIGridMaskTemplate = '[Exp]/ROIS/rEPI_MASK_NOEYES.img'; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Type of output +%%% images - output R and Z images of correlation with each seed +%%% maps - output R and P matrix of correlations between seeds +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ROIOutput = 'images'; + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% "maps" output mode only - save ROI time courses +%%% 1 - save ROI time courses to same location as R and P matrices +%%% 0 - do not save ROI time courses +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +saveroiTC = 0; + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Do not edit below this line +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%DEVSTART +mcRoot = fullfile(fileparts(mfilename('fullpath')),'../../MethodsCore'); +%DEVSTOP + +%[DEVmcRootAssign] + +addpath(fullfile(mcRoot,'matlabScripts')); +addpath(fullfile(mcRoot,'som')); +addpath(fullfile(mcRoot,'spm8')); + +som_batch_mc_central diff --git a/som/som_xyz.m b/som/som_xyz.m new file mode 100755 index 00000000..f6866908 --- /dev/null +++ b/som/som_xyz.m @@ -0,0 +1,66 @@ +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% +% Robert C. Welsh +% Copyright 2006 +% +% +% Returns the XYZ matrix for a 3D volumes +% +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function XYZ = SOM_XYZ(varargin) + +XYZ = []; + +if nargin < 1 + fprintf('You must specify a file name or header or dimensions.\n'); + return +end + +% Now see if a header, file name, or dimensions. + +if isa(varargin{1},'char') % File name + if exist(varargin{1}) == 2 % Yup a file name.) + theHeader = spm_vol(varargin{1}); + theDIM = theHeader.dim(1:3); + else + fprintf('Seems like you specified a file, but doesn''t exist.\n'); + return + end +elseif isa(varargin{1},'double') % Specified dimensions. + if length(varargin{1}) == 3 % Must be 3-d. + theDIM = varargin{1}; + else + fprintf('You must specify 3 axes.\n'); + return + end +elseif isa(varargin{1},'struct') % Header structure. + if isfield(varargin{1}.dim) % Does the dimension exist. + theDIM = varargin{1}.dim(1:3); + else + fprintf('Dimensions are missing.\n'); + return + end +end + +% +% Now make the matrix. +% + +XYZx = [1:theDIM(1)]'*ones(theDIM(2),1)'; +XYZy = ones(theDIM(1),1)*[1:theDIM(2)]; + +XYZ = zeros(3,prod(theDIM(1:3))); + +for iZ = 1:theDIM(3) + XYZz = iZ*ones(theDIM(1),theDIM(2)); + XYZ(:,(iZ-1)*prod(theDIM(1:2))+1:iZ*prod(theDIM(1:2))) = ... + [reshape(XYZx,[1 prod(size(XYZx))]);... + reshape(XYZy,[1 prod(size(XYZy))]);... + reshape(XYZz,[1 prod(size(XYZz))])]; +end + +% +% All done. +% + diff --git a/som/sphere_iter.m b/som/sphere_iter.m new file mode 100755 index 00000000..45a0135a --- /dev/null +++ b/som/sphere_iter.m @@ -0,0 +1,109 @@ +function [b,v,t] = sphere_iter(niter,type) + +t = ((1+sqrt(5))/2)/sqrt(1+((1+sqrt(5))/2)^2); +u = 1/sqrt(1+((1+sqrt(5))/2)^2); +ico_vertices = [t,u,0;... + -t,u,0;... + -t,-u,0;... + t,-u,0;... + u,0,t;... + u,0,-t;... + -u,0,-t;... + -u,0,t;... + 0,t,u;... + 0,-t,u;... + 0,-t,-u;... + 0,t,-u]; + +ico_triangles = [5,9,8;... + 5,8,10;... + 6,7,12;... + 6,11,7;... + 1,5,4;... + 1,4,6;... + 3,8,2;... + 3,2,7;... + 9,1,12;... + 9,12,2;... + 10,11,4;... + 10,3,11;... + 9,5,1;... + 12,1,6;... + 5,10,4;... + 6,4,11;... + 8,9,2;... + 7,2,12;... + 8,3,10;... + 7,11,3]; + +sqrt3 = sqrt(3); +tet_vertices = [sqrt3,sqrt3,sqrt3;... + -sqrt3,-sqrt3,sqrt3;... + -sqrt3,sqrt3,-sqrt3;... + sqrt3,-sqrt3,-sqrt3]; + +tet_triangles = [1,2,3;... + 1,4,2;... + 3,2,4;... + 4,1,3]; + +oct_vertices = [1,0,0;... + -1,0,0;... + 0,1,0;... + 0,-1,0;... + 0,0,1;... + 0,0,-1]; + +oct_triangles = [1,5,3;... + 3,5,2;... + 2,5,4;... + 4,5,1;... + 1,3,6;... + 3,2,6;... + 2,4,6;... + 4,1,6]; + +if(type < 2) + vertices = oct_vertices; + triangles = oct_triangles; +elseif(type < 3) + vertices = tet_vertices; + triangles = tet_triangles; +else + vertices = ico_vertices; + triangles = ico_triangles; +end + +for ii=1:niter + sub_triangles = zeros(size(triangles)); + for jj=1:length(triangles) + for kk=1:3 + if(sub_triangles(jj,kk) == 0) + v1 = triangles(jj,kk); + v2 = triangles(jj,mod(kk,3)+1); + v = (vertices(v1,:)+vertices(v2,:))/2; + v = v/sqrt(sum(v.^2)); + vertices = [vertices;v]; + sub_triangles(jj,kk) = length(vertices); + vsort = sort([v1,v2]); + for ll=1:length(triangles) + for mm=1:3 + if(sort([triangles(ll,mm),triangles(ll,mod(mm,3)+1)]) == vsort) + sub_triangles(ll,mm) = length(vertices); + end + end + end + end + end + end + triangles_temp = zeros(length(triangles)*4,3); + for jj=1:length(triangles) + triangles_temp((jj-1)*4+1,:) = [triangles(jj,1) sub_triangles(jj,1) sub_triangles(jj,3)]; + triangles_temp((jj-1)*4+2,:) = [sub_triangles(jj,1) sub_triangles(jj,2) sub_triangles(jj,3)]; + triangles_temp((jj-1)*4+3,:) = [sub_triangles(jj,1) triangles(jj,2) sub_triangles(jj,2)]; + triangles_temp((jj-1)*4+4,:) = [sub_triangles(jj,3) sub_triangles(jj,2) triangles(jj,3)]; + end + triangles = triangles_temp; +end +v = vertices; +b = sparse([triangles(:,1);triangles(:,2);triangles(:,3)],[triangles(:,2);triangles(:,3);triangles(:,1)],1); \ No newline at end of file diff --git a/som/sphere_make.m b/som/sphere_make.m new file mode 100755 index 00000000..ac3c9e02 --- /dev/null +++ b/som/sphere_make.m @@ -0,0 +1,20 @@ +function v = sphere_make(nPnts,nIter) + +v = randn(nPnts,3); +v = SOM_UnitNormMatrix(v,2); + +weight = 1/nPnts; + +diff_vect = zeros(nPnts,nPnts,3); + +for ii=1:nIter + vdotv = v*v'; + dist = (acos(vdotv)).^2; + v_rep_x = repmat(reshape(v,[nPnts,1,3]),[1,nPnts,1]); + v_rep_y = repmat(reshape(v,[1,nPnts,3]),[nPnts,1,1]); + diff_vect = cross(v_rep_x,cross(v_rep_x,v_rep_y,3),3); + diff_vect = diff_vect./repmat(dist,[1,1,3]); + diff_vect = weight*diff_vect; + diff_vect(find(isnan(diff_vect))) = 0; + v = SOM_UnitNormMatrix(v + squeeze(sum(diff_vect,2)),2); +end diff --git a/som/tile.m b/som/tile.m new file mode 100755 index 00000000..f76831f5 --- /dev/null +++ b/som/tile.m @@ -0,0 +1,29 @@ +function tmap=tile(maps); + +xsize=size(maps,1); +ysize=size(maps,2); +zsize=size(maps,3); + +sqsize=ceil(sqrt(zsize)); + +repnum=floor(zsize/sqsize); + +extranum=zsize-repnum*sqsize; + +temp=zeros(sqsize*xsize,sqsize*ysize); + +for rownum=1:repnum +%size(temp(((1:xsize)+(rownum-1)*xsize),:)) +%size(maps(:,:,((1:sqsize)+((rownum-1)*sqsize)))) + +temp(((1:xsize)+(rownum-1)*xsize),:)=reshape(maps(:,:,((1:sqsize)+((rownum-1)*sqsize))),[xsize ysize*sqsize]); +end; +%disp('hi') +for xnum=1:extranum +%disp('hi') +temp(((1:xsize)+repnum*xsize),((1:ysize)+((xnum-1)*ysize)))=maps(:,:,((repnum*sqsize)+xnum)); +end; + +tmap=temp; + +