-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Major updates to HRF_Est_Toolbox2 to allow passing in of SPM.mat file…
…s to estimate the HRF
- Loading branch information
1 parent
ae41e6d
commit 1d1cdf6
Showing
11 changed files
with
1,072 additions
and
358 deletions.
There are no files selected for viewing
700 changes: 433 additions & 267 deletions
700
CanlabCore/HRF_Est_Toolbox2/EstHRF_inAtlas/EstimateHRF_inAtlas.m
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
CanlabCore/HRF_Est_Toolbox2/EstHRF_inAtlas/extractHRF.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
function [HRF, tc] = extractHRF(HRF_OBJ, CondNames, at, rois) | ||
% Passed in HRF_OBJ is a cell array of fmri_data for each condition. | ||
% CondNames should be a cell array of charstr for each condition | ||
|
||
|
||
% Initialize the parallel pool if it's not already running | ||
if isempty(gcp('nocreate')) | ||
parpool; | ||
end | ||
|
||
% Preallocation of tc_local before the parfor loop | ||
HRF= cell(1, numel(rois)); | ||
tc= cell(1, numel(rois)); | ||
|
||
CondNames=matlab.lang.makeValidName(CondNames); | ||
|
||
% Now, handle the fourth dimension which varies with 'd' | ||
numCondNames = numel(CondNames); | ||
|
||
% HRF_local{d} = cell(1, numel(rois)); | ||
% tc_local{d} = cell(1, numel(rois)); | ||
|
||
% Consider doing apply_parcellation instead of mean(apply_mask(HRF_OBJ{c}, at.select_atlas_subset(rois(r), 'exact')).dat); | ||
% [parcel_means, parcel_pattern_expression, parcel_valence, rmsv_pos, rmsv_neg] = apply_parcellation(dat,at); | ||
% nps=load_image_set('npsplus'); | ||
% nps = get_wh_image(nps,1); | ||
% [parcel_means, parcel_pattern_expression, parcel_valence, rmsv_pos, rmsv_neg] = apply_parcellation(dat,at, 'pattern_expression', nps); | ||
% r=region(at,'unique_mask_values'); | ||
% wh_parcels=~all(isnan(parcel_means)) | ||
|
||
for r=1:numel(rois) | ||
HRF{r} = struct; | ||
tc{r} = cell(1, numCondNames); | ||
tic | ||
for c=1:numel(CondNames) | ||
|
||
try | ||
tc{r}{c}=mean(apply_mask(HRF_OBJ{c}, at.select_atlas_subset(rois(r), 'exact')).dat); | ||
|
||
HRF{r}.(CondNames{c}).model=tc{r}{c}; | ||
[HRF{r}.(CondNames{c}).peaks, HRF{r}.(CondNames{c}).troughs]=detectPeaksTroughs(tc{r}{c}', false); | ||
|
||
[~, regionVoxNum, ~, ~]=at.select_atlas_subset(rois(r), 'exact').get_region_volumes; | ||
HRF{r}.(CondNames{c}).model_voxnormed=tc{r}{c}/regionVoxNum; | ||
[HRF{r}.(CondNames{c}).peaks_voxnormed, HRF{r}.(CondNames{c}).troughs_voxnormed]=detectPeaksTroughs(tc{r}{c}'/regionVoxNum, false); | ||
catch | ||
% disp(t); | ||
disp(c); | ||
disp(rois{r}); | ||
tc{r}{c} | ||
mean(apply_mask(HRF_OBJ{c}, at.select_atlas_subset(rois(r), 'exact')).dat) | ||
{apply_mask(HRF_OBJ{c}, at.select_atlas_subset(rois(r), 'exact')).dat} | ||
HRF_OBJ | ||
|
||
end | ||
|
||
% Number of phases | ||
start_times = [HRF{r}.(CondNames{c}).peaks.start_time, HRF{r}.(CondNames{c}).troughs.start_time]; | ||
end_times = [HRF{r}.(CondNames{c}).peaks.end_time, HRF{r}.(CondNames{c}).troughs.end_time]; | ||
phases = [start_times(:), end_times(:)]; | ||
unique_phases = unique(phases, 'rows'); | ||
HRF{r}.(CondNames{c}).phases = mat2cell(unique_phases, ones(size(unique_phases, 1), 1), 2); | ||
|
||
% Loop over phases | ||
for p = 1:numel(HRF{r}.(CondNames{c}).phases) | ||
features = {'peaks', 'troughs'}; | ||
for f = 1:2 | ||
feat = features{f}; | ||
|
||
% Count the number of features (peaks or troughs) | ||
start_times = cell2mat({HRF{r}.(CondNames{c}).(feat).start_time}); | ||
current_phase_start = HRF{r}.(CondNames{c}).phases{p}(1); | ||
HRF{r}.(CondNames{c}).phase(p).(feat) = sum(start_times == current_phase_start); | ||
|
||
if HRF{r}.(CondNames{c}).phase(p).(feat) > 0 | ||
display(['Estimating ' , '_', rois{r}, '_', CondNames{c}, '_', ' Now...!']); | ||
display(['Phase ' , num2str(p), 'Feature ', feat]); | ||
idx = find(start_times == current_phase_start); | ||
auc = cell2mat({HRF{r}.(CondNames{c}).(feat).AUC}); | ||
height = cell2mat({HRF{r}.(CondNames{c}).(feat).height}); | ||
time_to_peak = cell2mat({HRF{r}.(CondNames{c}).(feat).time_to_peak}); | ||
half_height = cell2mat({HRF{r}.(CondNames{c}).(feat).half_height}); | ||
|
||
feat_voxnormed = strcat(feat, '_voxnormed'); | ||
auc_voxnormed = cell2mat({HRF{r}.(CondNames{c}).(feat_voxnormed).AUC}); | ||
height_voxnormed = cell2mat({HRF{r}.(CondNames{c}).(feat_voxnormed).height}); | ||
time_to_peak_voxnormed = cell2mat({HRF{r}.(CondNames{c}).(feat_voxnormed).time_to_peak}); | ||
half_height_voxnormed = cell2mat({HRF{r}.(CondNames{c}).(feat_voxnormed).half_height}); | ||
|
||
HRF{r}.(CondNames{c}).phase(p).auc = unique(auc(idx)); | ||
HRF{r}.(CondNames{c}).phase(p).auc_voxnormed = unique(auc_voxnormed(idx)); | ||
|
||
HRF{r}.(CondNames{c}).phase(p).height = height(idx); | ||
HRF{r}.(CondNames{c}).phase(p).height_voxnormed = height_voxnormed(idx); | ||
HRF{r}.(CondNames{c}).phase(p).time_to_peak = time_to_peak(idx); | ||
HRF{r}.(CondNames{c}).phase(p).time_to_peak_voxnormed = time_to_peak_voxnormed(idx); | ||
HRF{r}.(CondNames{c}).phase(p).half_height = half_height(idx); | ||
HRF{r}.(CondNames{c}).phase(p).half_height_voxnormed = half_height_voxnormed(idx); | ||
end | ||
end | ||
end | ||
end | ||
display(strjoin({' Done in ', num2str(toc), ' seconds with ', rois{r}})); | ||
|
||
end | ||
|
||
|
||
% temp_HRF_fit = HRF_local; | ||
% Save the results for this ROI | ||
% display([num2str(t), ' Done!']) | ||
% temp_HRF_fit{t} = HRF_local; | ||
% tc{t} = tc; | ||
|
||
|
||
% Transfer the results from the temporary cell array to the HRF structure | ||
% HRF.fit = temp_HRF_fit; | ||
% HRF.params=HRF_PARAMS; | ||
|
||
delete(gcp('nocreate')); | ||
|
||
|
||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
function [params_obj, hrf_obj, params_obj_dat, hrf_obj_dat, info] = hrf_fit(SPM,T,method,mode) | ||
% HRF estimation on fmri_data class object | ||
% | ||
% HRF estimation function for a single voxel; | ||
% | ||
% Implemented methods include: IL-model (Deterministic/Stochastic), FIR | ||
% (Regular/Smooth), and HRF (Canonical/+ temporal/+ temporal & dispersion). | ||
% With SPM.mat, TR, and experimental design are imported. | ||
% | ||
% :Inputs: | ||
% | ||
% **SPM** | ||
% SPM.mat file | ||
% | ||
% **T** | ||
% length of estimated HRF ij seconds | ||
% | ||
% **type** | ||
% Model type: 'FIR', 'IL', or 'CHRF' | ||
% | ||
% **mode** | ||
% Mode | ||
% | ||
% :Model Types: | ||
% | ||
% A. **Fit HRF using IL-function** | ||
% Choose mode (deterministic/stochastic) | ||
% - 0 - deterministic aproach | ||
% - 1 - simulated annealing approach | ||
% | ||
% Please note that when using simulated annealing approach you | ||
% may need to perform some tuning before use. | ||
% | ||
% B. **Fit HRF using FIR-model** | ||
% Choose mode (FIR/sFIR) | ||
% - 0 - FIR | ||
% - 1 - smooth FIR | ||
% | ||
% C. **Fit Canonical HRF** | ||
% Choose mode (FIR/sFIR) | ||
% - 0 - FIR | ||
% - 1 - smooth FIR | ||
% | ||
% | ||
% .. | ||
% Created by Michael Sun on 02/20/24 | ||
% .. | ||
|
||
if isstring(SPM) || ischar(SPM) | ||
|
||
load(SPM); | ||
if ~exist('SPM', 'var') | ||
error('Passed in filepath is not an SPM.mat file.') | ||
end | ||
|
||
end | ||
|
||
|
||
if isstruct(SPM) | ||
fnames=unique({SPM.xY.VY.fname})'; | ||
|
||
parfor i=1:numel(fnames) | ||
|
||
if contains(fnames{i}, '/') && ispc | ||
% PC Path conversion from Unix | ||
if strcmp(fnames{i}(1,1:2), '\\') | ||
d{i}=fmri_data(strrep(fnames{i}, '/', '\')); | ||
else | ||
d{i}=fmri_data(['\',strrep(fnames{i}, '/', '\')]); | ||
end | ||
|
||
else | ||
d{i}=fmri_data(fnames{i}); | ||
end | ||
end | ||
|
||
% Transform timeseries data into the way SPM desires. | ||
gkwy_d=spmify(d,SPM); | ||
|
||
% Extract TR | ||
TR = SPM.xY.RT; | ||
|
||
if isempty(T) | ||
disp('T is empty, generating T as 2 times the maximum duration for each task regressor.'); | ||
for i = 1:numel(SPM.Sess) | ||
T{i}=cellfun(@(cellArray) 2*ceil(max(cellArray)), {SPM.Sess(i).U.dur}); | ||
end | ||
elseif numel(SPM.Sess)>1 && ~iscell(T) | ||
% error('SPM structures concatenating multiple runs must have time windows passed in with a matching number of cell arrays.'); | ||
T=repmat({T}, 1, numel(SPM.Sess)); | ||
elseif iscell(T) && numel(SPM.Sess)~=numel(T) | ||
error('Incompatible number of runs in SPM and in cell-array T.') | ||
end | ||
|
||
% %% ONE WAY | ||
% % run hrf_fit separately for every d | ||
for i=1:numel(d) | ||
% Reassign data. | ||
d{i}.dat=gkwy_d{i}; | ||
|
||
% Extract TR | ||
TR = SPM.xY.RT; | ||
Runc=generateConditionTS(numel(SPM.Sess(i).row), [SPM.Sess(i).U.name], {SPM.Sess(i).U.ons}, {SPM.Sess(i).U.dur}); | ||
|
||
% There's a need to pull apart the SPM design matrix for every run | ||
% Column of regressors + covariates for each session + intercept | ||
% X=SPM.xX.X(SPM.Sess(i).row, [SPM.Sess(i).col, SPM.xX.iB(i)]); | ||
|
||
% Trouble is, X is not filtered | ||
% X=spm_filter(SPM.xX.K(i), SPM.xX.X(SPM.Sess(i).row, [SPM.Sess(i).col, SPM.xX.iB(i)])); | ||
|
||
% Trouble here is, task regressors may have to be re-generated for | ||
% FIR and sFIR if the original design matrix is cHRF, so check the | ||
% basis function | ||
if strcmpi(method, 'FIR') && ~contains(SPM.xBF.name, 'FIR') | ||
disp('Passed in SPM structure is not FIR. Reconstructing task-regressors for FIR fit'); | ||
|
||
len = numel(SPM.Sess(i).row); | ||
|
||
% Task regressors for each run can be found here: | ||
numstim=numel(SPM.Sess(i).U); | ||
|
||
% Make the design matrix: | ||
DX_all = cell(1, numstim); % Store DX matrices for each condition | ||
tlen_all = zeros(1, numstim); % Store tlen for each condition | ||
|
||
for s=1:numstim | ||
t = 1:TR:T{i}(s); | ||
tlen_all(s) = length(t); | ||
DX_all{s} = tor_make_deconv_mtx3(Runc(:,s), tlen_all(s), 1); | ||
end | ||
DX = horzcat(DX_all{:}); | ||
|
||
% due to horzcat, we will have multiple intercepts in this design matrix | ||
% therefore, we'll find the intercepts, drop them, and add an intercept at the end of the matrix | ||
intercept_idx = find(sum(DX)==len); | ||
copyDX = DX; | ||
copyDX(:,intercept_idx) = []; | ||
DX = [copyDX ones(len,1)]; | ||
|
||
% Covariate Design Matrix for each session (without intercept): | ||
NX=[SPM.Sess(i).C.C]; | ||
|
||
% Concatenate the Task regressors with Covariates | ||
X=[DX, NX]; | ||
% Filter | ||
X=spm_filter(SPM.xX.K(i), X); | ||
|
||
else | ||
% Otherwise set X to be the filtered design matrix of that | ||
% section. | ||
X=spm_filter(SPM.xX.K(i), SPM.xX.X(SPM.Sess(i).row, [SPM.Sess(i).col, SPM.xX.iB(i)])); | ||
end | ||
|
||
[params_obj{i}, hrf_obj{i}, params_obj_dat{i}, hrf_obj_dat{i}] = hrf_fit(d{i},TR,Runc,T{i},method,mode,X); | ||
|
||
info{i}.X=X; | ||
info{i}.names=[SPM.Sess(i).U.name]'; | ||
|
||
end | ||
|
||
end | ||
|
||
end |
Oops, something went wrong.