-
Notifications
You must be signed in to change notification settings - Fork 15
/
generate_spm_singletrial.m
348 lines (286 loc) · 14.9 KB
/
generate_spm_singletrial.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
function [] = generate_spm_singletrial()
% This script takes an existing SPM.mat file and converts it to a
% single-trial SPM.mat file, where each trial is modeled as a separate
% regressor. All other parameters (explicit mask, covariates, etc) are
% pulled in identically from the original SPM.mat file.
%
% Setting the 'estimate' flag to a non-zero value will automatically
% estimate the newly-generated SPM.mat file.
%
% A beta_info.mat file is generated, specifying condition information for
% each of the resulting betas.
%
% This script has two modes: multi-regressor and multi-model. The multi-
% regressor approach estimates a single model with all trials represented
% by individual regressors. The multi-model approach estimates a model for
% each individual trial, setting the first regressor to the individual
% trial and the second to all other trials. Beta images are then moved and
% renamed in a single betas directory (with the option to get rid of all of
% the extra files). The multi-regressor approach is similar to that
% described in Rissman et al. 2004 NI, and the multi-model approach is
% similar to the LS-S approach described in Mumford et al. 2012 NI. Note
% that the multi-model approach is takes a long time to run in this
% implementation, so there's a lot of room for improvement.
%
% Requires SPM8.
%
% To-do list: contrast vectors for original conditions (for comparison
% against conventional model), option to estimate contrasts for each trial
% so that t-images can be used instead of betas, option to use
% files with a different prefix than original model (e.g., unsmoothed data)
%
% Note: The multi-regressor approach, as implemented here, has been
% debugged with my own data, but not the multi-model approach. I include
% the multi-model code here for completeness, but if you use it, debug and
% use with caution.
%
%
% Author: Maureen Ritchey, 10-2012
%% USER INFORMATION
% Directory for the new model. If it does not exist, it will be created.
stdir = '/Users/YourName/Project/Analysis/SingleTrialModel/';
% Directory for the existing model
modeldir = '/Users/YourName/Project/Analysis/ConventionalModel/';
% Subjects to run
subjects = {'s01' 's02' 's03' 's04' 's05' 's06' 's07' 's08' 's09' 's10'};
% Flag for estimating model (0=no estimation, 1=estimation)
estimate = 1;
% If desired, specify a condition name to lump together (no indiv trial
% regressors). This is useful if the original SPM.mat model included a
% 'catch-all' condition of no interest. Set to 'NONE' if not desired.
lump_conditions = 'NONE'; % 'OTHERS'
% Type of model to run (1=multi-regressor approach, 2=multi-model approach)
modeltype = 1;
% Option to discard extra files in multi-model approach to save space
% (1=discard, 0=leave them). Always keeps regs and covs files.
discard_mm_files = 1;
%% MAIN CODE
for iSub = 1:length(subjects)
%load pre-existing SPM file containing model information
fprintf('\nLoading previous model for %s:\n%s\n',subjects{iSub},[modeldir,subjects{iSub},'/SPM.mat']);
if exist([modeldir,subjects{iSub},'/SPM.mat'],'file')
load([modeldir,subjects{iSub},'/SPM.mat'])
else
error('Cannot find SPM.mat file.');
end
%find or create the output directory for the single-trial model
outputdir = [stdir subjects{iSub} '/'];
if ~exist(outputdir,'dir')
fprintf('\nCreating directory:\n%s\n',outputdir);
mkdir(outputdir)
end
%get model information from SPM file
fprintf('\nGetting model information...\n');
files = SPM.xY.P;
fprintf('Modeling %i timepoints across %i sessions.\n',size(files,1),length(SPM.Sess));
% MULTI-REGRESSOR APPROACH
if modeltype == 1
%set up beta information
trialinfo = {'beta_number' 'session' 'condition' 'condition_rep' 'number_onsets' 'first_onset' 'beta_name'};
counter = 1;
%loop across sessions
for iSess=1:length(SPM.Sess)
rows = SPM.Sess(iSess).row;
sess_files = files(rows',:);
sess_files = cellstr(sess_files);
covariates = SPM.Sess(iSess).C.C;
onsets = {};
durations = {};
names = {};
for j=1:length(SPM.Sess(iSess).U)
%check for special condition names to lump together
if strfind(SPM.Sess(iSess).U(j).name{1},lump_conditions)
onsets = [onsets SPM.Sess(iSess).U(j).ons'];
durations = [durations SPM.Sess(iSess).U(j).dur'];
cur_name = [SPM.Sess(iSess).U(j).name{1}];
names = [names cur_name];
curinfo = {counter iSess SPM.Sess(iSess).U(j).name{1} [1] length(SPM.Sess(iSess).U(j).ons) SPM.Sess(iSess).U(j).ons(1) cur_name};
trialinfo = [trialinfo; curinfo];
counter = counter + 1;
%otherwise set up a regressor for each individual trial
else
for k=1:length(SPM.Sess(iSess).U(j).ons)
onsets = [onsets SPM.Sess(iSess).U(j).ons(k)];
durations = [durations SPM.Sess(iSess).U(j).dur(k)];
cur_name = [SPM.Sess(iSess).U(j).name{1} '_' num2str(k)];
names = [names cur_name];
curinfo = {counter iSess SPM.Sess(iSess).U(j).name{1} k length(SPM.Sess(iSess).U(j).ons(k)) SPM.Sess(iSess).U(j).ons(k) cur_name};
trialinfo = [trialinfo; curinfo];
counter = counter + 1;
end
end
end
%save regressor onset files
fprintf('Saving regressor onset files for Session %i: %i trials included\n',iSess,length(names));
regfile = [outputdir 'st_regs_run' num2str(iSess) '.mat'];
save(regfile,'names','onsets','durations');
%save covariates (e.g., motion parameters) that were specified
%in the original model
covfile = [outputdir 'st_covs_run' num2str(iSess) '.txt'];
dlmwrite(covfile,covariates,'\t');
if ~isempty(covariates)
for icov = 1:size(covariates,2)
curinfo = {counter iSess 'covariate' icov 1 0 strcat('covariate',num2str(icov))};
trialinfo = [trialinfo; curinfo];
counter = counter + 1;
end
end
%create matlabbatch for creating new SPM.mat file
if iSess==1
matlabbatch = create_spm_init(outputdir,SPM);
end
matlabbatch = create_spm_sess(matlabbatch,iSess,sess_files,regfile,covfile,SPM);
end
%run matlabbatch to create new SPM.mat file using SPM batch tools
fprintf('\nCreating SPM.mat file:\n%s\n',[outputdir 'SPM.mat']);
spm_jobman('initcfg')
spm('defaults', 'FMRI');
spm_jobman('serial', matlabbatch);
if estimate > 0 %optional: estimate SPM model
fprintf('\nEstimating model from SPM.mat file.\n');
spmfile = [outputdir 'SPM.mat'];
matlabbatch = estimate_spm(spmfile);
spm_jobman('serial', matlabbatch);
end
%save beta information
infofile = [outputdir 'beta_info.mat'];
save(infofile,'trialinfo');
clear SPM matlabbatch
% MULTI-MODEL APPROACH - use with caution! may require debugging
elseif modeltype == 2
%make trial directory
betadir = [outputdir 'betas/'];
if ~exist(betadir,'dir')
mkdir(betadir)
end
%set up beta information
trialinfo = {'beta_number' 'session' 'condition' 'condition_rep' 'number_onsets' 'beta_name' 'trial_dir' 'beta_name'};
counter = 1;
%loop across sessions
for iSess=1:length(SPM.Sess)
rows = SPM.Sess(iSess).row;
sess_files = files(rows',:);
sess_files = cellstr(sess_files);
covariates = SPM.Sess(iSess).C.C;
for j=1:length(SPM.Sess(iSess).U)
%check for special condition names to ignore
if strfind(SPM.Sess(iSess).U(j).name{1},lump_conditions)
if strcmp(lump_conditions,'NONE')
else
fprintf('\nIgnoring condition: %s\n',lump_conditions);
end
%otherwise set up a model for each individual trial
else
for k=1:length(SPM.Sess(iSess).U(j).ons)
% individual trial
onsets = {};
durations = {};
names = {};
onsets = [onsets SPM.Sess(iSess).U(j).ons(k)];
durations = [durations SPM.Sess(iSess).U(j).dur(k)];
cur_name = [SPM.Sess(iSess).U(j).name{1} '_' num2str(k)];
names = [names cur_name];
% all other trials
otheronsets = [];
otherdurations = [];
othernames = {};
for jj=1:length(SPM.Sess(iSess).U)
for kk=1:length(SPM.Sess(iSess).U(jj).ons)
if j~=jj & k~=kk
otheronsets = [otheronsets SPM.Sess(iSess).U(jj).ons(kk)];
otherdurations = [otherdurations SPM.Sess(iSess).U(jj).dur(kk)];
othernames = ['OTHERTRIALS'];
end
end
end
% group into single set of regs files
onsets = [onsets otheronsets];
durations = [durations otherdurations];
names = [names othernames];
%make trial directory
trialdir = [outputdir 'Sess' num2str(iSess) '/' cur_name '/'];
if ~exist(trialdir,'dir')
mkdir(trialdir)
end
%add trial information
curinfo = {counter iSess SPM.Sess(iSess).U(j).name{1} k length(SPM.Sess(iSess).U(j).ons(k)) cur_name trialdir ['Sess' num2str(iSess) '_' cur_name '.img']};
trialinfo = [trialinfo; curinfo];
counter = counter + 1;
%save regressor onset files
regfile = [trialdir 'st_regs.mat'];
save(regfile,'names','onsets','durations');
covfile = [trialdir 'st_covs.txt'];
dlmwrite(covfile,covariates,'\t');
%create matlabbatch for creating new SPM.mat file
matlabbatch = create_spm_init(trialdir,SPM);
matlabbatch = create_spm_sess(matlabbatch,1,sess_files,regfile,covfile,SPM);
%run matlabbatch to create new SPM.mat file using SPM batch tools
fprintf('\nCreating SPM.mat file:\n%s\n\n',[trialdir 'SPM.mat']);
if counter == 1
spm_jobman('initcfg')
spm('defaults', 'FMRI');
end
spm_jobman('serial', matlabbatch);
if estimate %optional: estimate SPM model
fprintf('\nEstimating model from SPM.mat file.\n');
spmfile = [trialdir 'SPM.mat'];
matlabbatch = estimate_spm(spmfile);
spm_jobman('serial', matlabbatch);
clear matlabbatch
% copy first beta image to beta directory
copyfile([trialdir 'beta_0001.img'],[betadir 'Sess' num2str(iSess) '_' cur_name '.img']);
copyfile([trialdir 'beta_0001.hdr'],[betadir 'Sess' num2str(iSess) '_' cur_name '.hdr']);
% discard extra files, if desired
if discard_mm_files
prevdir = pwd;
cd(trialdir);
delete SPM*; delete *.hdr; delete *.img;
cd(prevdir);
end
end
end
end
end
end
%save beta information
infofile = [betadir subjects{iSub} '_beta_info.mat'];
save(infofile,'trialinfo');
else
error('Specify model type as 1 or 2');
end
clear SPM
end
end
%% SUBFUNCTIONS
function [matlabbatch] = create_spm_init(outputdir,SPM)
% subfunction for initializing the matlabbatch structure to create the SPM
matlabbatch{1}.spm.stats.fmri_spec.dir = {outputdir};
matlabbatch{1}.spm.stats.fmri_spec.timing.units = SPM.xBF.UNITS;
matlabbatch{1}.spm.stats.fmri_spec.timing.RT = SPM.xY.RT;
matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t = SPM.xBF.T;
matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t0 = SPM.xBF.T0;
matlabbatch{1}.spm.stats.fmri_spec.fact = struct('name', {}, 'levels', {});
matlabbatch{1}.spm.stats.fmri_spec.bases.hrf.derivs = [0 0];
matlabbatch{1}.spm.stats.fmri_spec.volt = SPM.xBF.Volterra;
matlabbatch{1}.spm.stats.fmri_spec.global = 'None';
if isempty(SPM.xM.VM)
matlabbatch{1}.spm.stats.fmri_spec.mask = {''};
else
matlabbatch{1}.spm.stats.fmri_spec.mask = {SPM.xM.VM.fname};
end
matlabbatch{1}.spm.stats.fmri_spec.cvi = SPM.xVi.form;
end
function [matlabbatch] = create_spm_sess(matlabbatch,iSess,sess_files,regfile,covfile,SPM)
% subfunction for adding sessions to the matlabbatch structure
matlabbatch{1}.spm.stats.fmri_spec.sess(iSess).scans = sess_files; %fix this
matlabbatch{1}.spm.stats.fmri_spec.sess(iSess).cond = struct('name', {}, 'onset', {}, 'duration', {}, 'tmod', {}, 'pmod', {});
matlabbatch{1}.spm.stats.fmri_spec.sess(iSess).multi = {regfile};
matlabbatch{1}.spm.stats.fmri_spec.sess(iSess).regress = struct('name', {}, 'val', {});
matlabbatch{1}.spm.stats.fmri_spec.sess(iSess).multi_reg = {covfile};
matlabbatch{1}.spm.stats.fmri_spec.sess(iSess).hpf = SPM.xX.K(iSess).HParam;
end
function [matlabbatch] = estimate_spm(spmfile)
% subfunction for creating a matlabbatch structure to estimate the SPM
matlabbatch{1}.spm.stats.fmri_est.spmmat = {spmfile};
matlabbatch{1}.spm.stats.fmri_est.method.Classical = 1;
end