From cc07d9519c9f8ec0ec05a9199a0f0788a0e360c2 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Tue, 25 Feb 2025 17:00:01 +0100 Subject: [PATCH 01/12] tmp --- demos/MoAE/moae_03_slice_display.m | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/demos/MoAE/moae_03_slice_display.m b/demos/MoAE/moae_03_slice_display.m index e08c7a46..8ab8bdd5 100644 --- a/demos/MoAE/moae_03_slice_display.m +++ b/demos/MoAE/moae_03_slice_display.m @@ -31,18 +31,10 @@ opt.model.file = fullfile(this_dir, 'models', 'model-MoAE_smdl.json'); -% Specify the result to compute -opt.results(1).nodeName = 'run_level'; - -opt.results(1).name = 'listening'; -% MONTAGE FIGURE OPTIONS -opt.results(1).montage.do = true(); -opt.results(1).montage.slices = -4:2:16; % in mm -% axial is default 'sagittal', 'coronal' -opt.results(1).montage.orientation = 'axial'; -% will use the MNI T1 template by default but the underlay image can be changed. -opt.results(1).montage.background = ... - fullfile(spm('dir'), 'canonical', 'avg152T1.nii'); +% read the model +opt = checkOptions(opt); + +opt.results = opt.model.bm.Nodes{1}.Model.Software.bidspm.Results{1}; opt = checkOptions(opt); @@ -55,10 +47,6 @@ rightRoiFile = bids.query(BIDS_ROI, 'data', filter); -filter.hemi = 'L'; - -leftRoiFile = bids.query(BIDS_ROI, 'data', filter); - % we get the con image to extract data ffxDir = getFFXdir(subLabel, opt); maskImage = spm_select('FPList', ffxDir, '^.*_mask.nii$'); @@ -66,7 +54,7 @@ conImage = spm_select('FPList', ffxDir, ['^con_' bf.entities.label '.nii$']); %% Layers to put on the figure -layers = sd_config_layers('init', {'truecolor', 'dual', 'contour', 'contour'}); +layers = sd_config_layers('init', {'truecolor', 'dual', 'contour'}); % Layer 1: Anatomical map [anat_normalized_file, anatRange] = return_normalized_anat_file(opt, subLabel); @@ -100,20 +88,18 @@ %% Layer 3 and 4: Contour of ROI +roi = spm_select('FPList', ffxDir, ['^sub_.*' bf.entities.label '.*_mask.nii$']); + layers(3).color.file = rightRoiFile{1}; layers(3).color.map = [0 0 0]; layers(3).color.line_width = 2; -layers(4).color.file = leftRoiFile{1}; -layers(4).color.map = [1 1 1]; -layers(4).color.line_width = 2; - %% Settings settings = sd_config_settings('init'); % we reuse the details for the SPM montage +settings.slice.disp_slices = opt.results(1).montage.slices; settings.slice.orientation = opt.results(1).montage.orientation; -settings.slice.disp_slices = -15:3:18; settings.fig_specs.n.slice_column = 4; settings.fig_specs.title = opt.results(1).name; From 4b25c7fced52777d79bcd5da232d0a94369344e3 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Wed, 5 Mar 2025 12:08:01 +0100 Subject: [PATCH 02/12] [DATALAD] Recorded changes --- demos/MoAE/moae_03_slice_display.m | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/demos/MoAE/moae_03_slice_display.m b/demos/MoAE/moae_03_slice_display.m index 8ab8bdd5..b39e5d8d 100644 --- a/demos/MoAE/moae_03_slice_display.m +++ b/demos/MoAE/moae_03_slice_display.m @@ -38,15 +38,6 @@ opt = checkOptions(opt); -use_schema = false; -BIDS_ROI = bids.layout(opt.dir.roi, 'use_schema', use_schema); - -filter = struct('sub', subLabel, ... - 'hemi', 'R', ... - 'desc', 'auditoryCortex'); - -rightRoiFile = bids.query(BIDS_ROI, 'data', filter); - % we get the con image to extract data ffxDir = getFFXdir(subLabel, opt); maskImage = spm_select('FPList', ffxDir, '^.*_mask.nii$'); @@ -60,7 +51,6 @@ [anat_normalized_file, anatRange] = return_normalized_anat_file(opt, subLabel); layers(1).color.file = anat_normalized_file; layers(1).color.range = [0 anatRange(2)]; - layers(1).color.map = gray(256); %% Layer 2: Dual-coded layer @@ -83,14 +73,12 @@ spmTImage = spm_select('FPList', ffxDir, ['^spmT_' bf.entities.label '.nii$']); layers(2).opacity.file = spmTImage; -layers(2).opacity.range = [2 3]; +layers(2).opacity.range = [0 3]; layers(2).opacity.label = '| t |'; %% Layer 3 and 4: Contour of ROI - -roi = spm_select('FPList', ffxDir, ['^sub_.*' bf.entities.label '.*_mask.nii$']); - -layers(3).color.file = rightRoiFile{1}; +contour = spm_select('FPList', ffxDir, ['^sub.*' bf.entities.label '.*_mask.nii']); +layers(3).color.file = contour; layers(3).color.map = [0 0 0]; layers(3).color.line_width = 2; From 32aa8ef9d7eaa57eacdc5625c8514aa2e4b05344 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Wed, 5 Mar 2025 14:25:33 +0100 Subject: [PATCH 03/12] refactor --- demos/MoAE/moae_03_slice_display.m | 21 +++++-- demos/MoAE/return_normalized_anat_file.m | 26 --------- src/stats/results/checkMontage.m | 70 ++++++++++++++++++++++++ src/workflows/stats/bidsResults.m | 59 -------------------- 4 files changed, 87 insertions(+), 89 deletions(-) delete mode 100644 demos/MoAE/return_normalized_anat_file.m create mode 100644 src/stats/results/checkMontage.m diff --git a/demos/MoAE/moae_03_slice_display.m b/demos/MoAE/moae_03_slice_display.m index b39e5d8d..72b5bc48 100644 --- a/demos/MoAE/moae_03_slice_display.m +++ b/demos/MoAE/moae_03_slice_display.m @@ -31,15 +31,25 @@ opt.model.file = fullfile(this_dir, 'models', 'model-MoAE_smdl.json'); +opt.subjects = [subLabel]; + % read the model opt = checkOptions(opt); -opt.results = opt.model.bm.Nodes{1}.Model.Software.bidspm.Results{1}; +iRes = 1; + +opt.results = opt.model.bm.Nodes{iRes}.Model.Software.bidspm.Results{1}; + +node = opt.model.bm.Nodes{iRes}; +[opt, BIDS] = checkMontage(opt, iRes, node, struct([]), subLabel); opt = checkOptions(opt); +opt.results(iRes).montage = setMontage(opt.results(iRes)); + % we get the con image to extract data ffxDir = getFFXdir(subLabel, opt); + maskImage = spm_select('FPList', ffxDir, '^.*_mask.nii$'); bf = bids.File(spm_file(maskImage, 'filename')); conImage = spm_select('FPList', ffxDir, ['^con_' bf.entities.label '.nii$']); @@ -48,9 +58,12 @@ layers = sd_config_layers('init', {'truecolor', 'dual', 'contour'}); % Layer 1: Anatomical map -[anat_normalized_file, anatRange] = return_normalized_anat_file(opt, subLabel); -layers(1).color.file = anat_normalized_file; -layers(1).color.range = [0 anatRange(2)]; +layers(1).color.file = opt.results(iRes).montage.background{1}; + +hdr = spm_vol(layers(1).color.file); +[max_val, min_val] = slover('volmaxmin', hdr); +layers(1).color.range = [0 max_val]; + layers(1).color.map = gray(256); %% Layer 2: Dual-coded layer diff --git a/demos/MoAE/return_normalized_anat_file.m b/demos/MoAE/return_normalized_anat_file.m deleted file mode 100644 index 6a786ac9..00000000 --- a/demos/MoAE/return_normalized_anat_file.m +++ /dev/null @@ -1,26 +0,0 @@ -function [anat_normalized_file, anat_range] = return_normalized_anat_file(opt, sub_label) - % - - % (C) Copyright 2021 Remi Gau - - [BIDS, opt] = getData(opt, opt.dir.preproc); - - anat_normalized_file = bids.query(BIDS, 'data', ... - 'modality', 'anat', ... - 'space', 'IXI549Space', ... - 'suffix', 'T1w'); - - if isempty(anat_normalized_file) - opt.query.space = 'IXI549Space'; - [anat_normalized_file, anatDataDir] = getAnatFilename(BIDS, opt, sub_label); - anat_normalized_file = fullfile(anatDataDir, anat_normalized_file); - else - anat_normalized_file = anat_normalized_file{1}; - end - - hdr = spm_vol(anat_normalized_file); - vol = spm_read_vols(hdr); - - anat_range = [min(vol(:)) max(vol(:))]; - -end diff --git a/src/stats/results/checkMontage.m b/src/stats/results/checkMontage.m new file mode 100644 index 00000000..72f6aa8b --- /dev/null +++ b/src/stats/results/checkMontage.m @@ -0,0 +1,70 @@ +function [opt, BIDS] = checkMontage(opt, iRes, node, BIDS, subLabel) + % + % Check values for create a slice montage. + % + % Set default values if they are missing. + % + % USAGE:: + % + % [opt, BIDS] = checkMontage(opt, iRes, node, BIDS, subLabel) + % + % + + % (C) Copyright 2019 bidspm developers + + if nargin < 4 + BIDS = ''; + subLabel = ''; + end + + if isfield(opt.results(iRes), 'montage') && any(opt.results(iRes).montage.do) + + background = opt.results(iRes).montage.background; + + % TODO refactor with getInclusiveMask + if isstruct(background) + + if ismember(lower(node.Level), {'run', 'session', 'subject'}) + + if isempty(BIDS) + BIDS = bids.layout(opt.dir.preproc, ... + 'use_schema', false, ... + 'index_dependencies', false, ... + 'filter', struct('sub', {opt.subjects})); + end + + background.sub = subLabel; + background.space = opt.space; + file = bids.query(BIDS, 'data', background); + + if iscell(file) + if isempty(file) + % let checkMaskOrUnderlay figure it out + file = ''; + + elseif numel(file) == 1 + file = file{1}; + + elseif numel(file) > 1 + file = file{1}; + + msg = sprintf('More than 1 overlay image found for %s.\n Taking the first one.', ... + bids.internal.create_unordered_list(background)); + id = 'tooManyMontageBackground'; + logger('WARNING', msg, 'id', id, 'options', opt, 'filename', mfilename()); + end + + end + + background = file; + + end + + end + + background = checkMaskOrUnderlay(background, opt, 'background'); + opt.results(iRes).montage.background = background; + + end + +end diff --git a/src/workflows/stats/bidsResults.m b/src/workflows/stats/bidsResults.m index e766fbec..9f85f6bb 100644 --- a/src/workflows/stats/bidsResults.m +++ b/src/workflows/stats/bidsResults.m @@ -547,62 +547,3 @@ matlabbatch{end}.result = result; end - -function [opt, BIDS] = checkMontage(opt, iRes, node, BIDS, subLabel) - - if nargin < 4 - BIDS = ''; - subLabel = ''; - end - - if isfield(opt.results(iRes), 'montage') && any(opt.results(iRes).montage.do) - - background = opt.results(iRes).montage.background; - - % TODO refactor with getInclusiveMask - if isstruct(background) - - if ismember(lower(node.Level), {'run', 'session', 'subject'}) - - if isempty(BIDS) - BIDS = bids.layout(opt.dir.preproc, ... - 'use_schema', false, ... - 'index_dependencies', false, ... - 'filter', struct('sub', {opt.subjects})); - end - - background.sub = subLabel; - background.space = opt.space; - file = bids.query(BIDS, 'data', background); - - if iscell(file) - if isempty(file) - % let checkMaskOrUnderlay figure it out - file = ''; - - elseif numel(file) == 1 - file = file{1}; - - elseif numel(file) > 1 - file = file{1}; - - msg = sprintf('More than 1 overlay image found for %s.\n Taking the first one.', ... - bids.internal.create_unordered_list(background)); - id = 'tooManyMontageBackground'; - logger('WARNING', msg, 'id', id, 'options', opt, 'filename', mfilename()); - end - - end - - background = file; - - end - - end - - background = checkMaskOrUnderlay(background, opt, 'background'); - opt.results(iRes).montage.background = background; - - end - -end From b878771d0fe3d17489be626258c4fcd4d81d91f9 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Wed, 5 Mar 2025 14:26:08 +0100 Subject: [PATCH 04/12] ref --- src/stats/results/checkMontage.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stats/results/checkMontage.m b/src/stats/results/checkMontage.m index 72f6aa8b..ecc34e96 100644 --- a/src/stats/results/checkMontage.m +++ b/src/stats/results/checkMontage.m @@ -1,7 +1,7 @@ function [opt, BIDS] = checkMontage(opt, iRes, node, BIDS, subLabel) - % + % % Check values for create a slice montage. - % + % % Set default values if they are missing. % % USAGE:: @@ -66,5 +66,5 @@ opt.results(iRes).montage.background = background; end - + end From 6df72957ecfb26a56d8bb5bec4c6aff1acb5decf Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Wed, 5 Mar 2025 16:48:33 +0100 Subject: [PATCH 05/12] reuse defaults --- demos/MoAE/moae_03_slice_display.m | 47 +++++++++++++++----------- src/defaults/defaultResultsStructure.m | 7 ++-- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/demos/MoAE/moae_03_slice_display.m b/demos/MoAE/moae_03_slice_display.m index 72b5bc48..84d71e0b 100644 --- a/demos/MoAE/moae_03_slice_display.m +++ b/demos/MoAE/moae_03_slice_display.m @@ -47,61 +47,68 @@ opt.results(iRes).montage = setMontage(opt.results(iRes)); +opt.results(iRes).sdConfig + % we get the con image to extract data ffxDir = getFFXdir(subLabel, opt); maskImage = spm_select('FPList', ffxDir, '^.*_mask.nii$'); bf = bids.File(spm_file(maskImage, 'filename')); -conImage = spm_select('FPList', ffxDir, ['^con_' bf.entities.label '.nii$']); + %% Layers to put on the figure layers = sd_config_layers('init', {'truecolor', 'dual', 'contour'}); % Layer 1: Anatomical map -layers(1).color.file = opt.results(iRes).montage.background{1}; +overwrite = true; +layers(1) = setFields(layers(1), opt.results(iRes).sdConfig.layers{1}, overwrite); -hdr = spm_vol(layers(1).color.file); -[max_val, min_val] = slover('volmaxmin', hdr); -layers(1).color.range = [0 max_val]; +% layers(1).color.file = opt.results(iRes).montage.background{1}; +% +% hdr = spm_vol(layers(1).color.file); +% [max_val, min_val] = slover('volmaxmin', hdr); +% layers(1).color.range = [0 max_val]; -layers(1).color.map = gray(256); %% Layer 2: Dual-coded layer % % - contrast estimates color-coded; +layers(2) = setFields(layers(2), opt.results(iRes).sdConfig.layers{2}, overwrite); +conImage = spm_select('FPList', ffxDir, ['^con_' bf.entities.label '.nii$']); layers(2).color.file = conImage; color_map_folder = fullfile(fileparts(which('map_luminance')), '..', 'mat_maps'); -load(fullfile(color_map_folder, 'diverging_bwr_iso.mat')); +load(fullfile(color_map_folder, layers(2).color.map)); layers(2).color.map = diverging_bwr; -layers(2).color.range = [-4 4]; -layers(2).color.label = '\beta_{listening} - \beta_{baseline} (a.u.)'; +% layers(2).color.range = [-4 4]; +% layers(2).color.label = '\beta_{listening} - \beta_{baseline} (a.u.)'; -%% Layer 2: Dual-coded layer -% -% - t-statistics opacity-coded +% - t-statistics opacity-coded spmTImage = spm_select('FPList', ffxDir, ['^spmT_' bf.entities.label '.nii$']); layers(2).opacity.file = spmTImage; -layers(2).opacity.range = [0 3]; -layers(2).opacity.label = '| t |'; +% layers(2).opacity.range = [0 3]; +% layers(2).opacity.label = '| t |'; %% Layer 3 and 4: Contour of ROI +layers(3) = setFields(layers(3), opt.results(iRes).sdConfig.layers{3}, overwrite); + contour = spm_select('FPList', ffxDir, ['^sub.*' bf.entities.label '.*_mask.nii']); + layers(3).color.file = contour; -layers(3).color.map = [0 0 0]; -layers(3).color.line_width = 2; +% layers(3).color.map = [0 0 0]; +% layers(3).color.line_width = 2; %% Settings -settings = sd_config_settings('init'); +settings = opt.results(iRes).sdConfig.settings; % we reuse the details for the SPM montage -settings.slice.disp_slices = opt.results(1).montage.slices; -settings.slice.orientation = opt.results(1).montage.orientation; -settings.fig_specs.n.slice_column = 4; +% settings.slice.disp_slices = opt.results(1).montage.slices; +% settings.slice.orientation = opt.results(1).montage.orientation; + settings.fig_specs.title = opt.results(1).name; %% Display the layers diff --git a/src/defaults/defaultResultsStructure.m b/src/defaults/defaultResultsStructure.m index 9bc93fed..aed18634 100644 --- a/src/defaults/defaultResultsStructure.m +++ b/src/defaults/defaultResultsStructure.m @@ -33,11 +33,12 @@ layers{2} = struct('color', struct('file', [], ... % con image 'map', 'diverging_bwr_iso', ... - 'range', [-4 4]), ... - 'label', '\beta_{listening} - \beta_{baseline} (a.u.)', ... + 'range', [-4 4], ... + 'label', '\beta_{listening} - \beta_{baseline} (a.u.)'), ... 'opacity', struct('file', [], ... % spmT image 'range', [2 3], ... - 'label', '| t |')); + 'label', '| t |'), ... + 'type', 'dual'); layers{3} = struct('color', struct('file', [], ... % spmT mask thresholded at 0.05 FWD 'map', [0 0 0], ... From 4f3894757f06e95a0902cbf64e9db8aaba6dbdcf Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 7 Mar 2025 17:54:19 +0100 Subject: [PATCH 06/12] refg --- demos/MoAE/moae_03_slice_display.m | 195 +++++++++++++++---------- src/defaults/defaultResultsStructure.m | 11 +- src/stats/results/renameSpmT.m | 7 +- 3 files changed, 129 insertions(+), 84 deletions(-) diff --git a/demos/MoAE/moae_03_slice_display.m b/demos/MoAE/moae_03_slice_display.m index 84d71e0b..3f65fc19 100644 --- a/demos/MoAE/moae_03_slice_display.m +++ b/demos/MoAE/moae_03_slice_display.m @@ -18,8 +18,6 @@ this_dir = fileparts(mfilename('fullpath')); -subLabel = '01'; - opt.pipeline.type = 'stats'; opt.dir.raw = fullfile(this_dir, 'inputs', 'raw'); @@ -31,85 +29,128 @@ opt.model.file = fullfile(this_dir, 'models', 'model-MoAE_smdl.json'); -opt.subjects = [subLabel]; +opt.subjects = {'01'}; % read the model opt = checkOptions(opt); -iRes = 1; - -opt.results = opt.model.bm.Nodes{iRes}.Model.Software.bidspm.Results{1}; - -node = opt.model.bm.Nodes{iRes}; -[opt, BIDS] = checkMontage(opt, iRes, node, struct([]), subLabel); - -opt = checkOptions(opt); - -opt.results(iRes).montage = setMontage(opt.results(iRes)); - -opt.results(iRes).sdConfig +% TODO loop over noce and subjects -% we get the con image to extract data -ffxDir = getFFXdir(subLabel, opt); - -maskImage = spm_select('FPList', ffxDir, '^.*_mask.nii$'); -bf = bids.File(spm_file(maskImage, 'filename')); - - -%% Layers to put on the figure -layers = sd_config_layers('init', {'truecolor', 'dual', 'contour'}); - -% Layer 1: Anatomical map overwrite = true; -layers(1) = setFields(layers(1), opt.results(iRes).sdConfig.layers{1}, overwrite); - -% layers(1).color.file = opt.results(iRes).montage.background{1}; -% -% hdr = spm_vol(layers(1).color.file); -% [max_val, min_val] = slover('volmaxmin', hdr); -% layers(1).color.range = [0 max_val]; - - -%% Layer 2: Dual-coded layer -% -% - contrast estimates color-coded; -layers(2) = setFields(layers(2), opt.results(iRes).sdConfig.layers{2}, overwrite); - -conImage = spm_select('FPList', ffxDir, ['^con_' bf.entities.label '.nii$']); -layers(2).color.file = conImage; - -color_map_folder = fullfile(fileparts(which('map_luminance')), '..', 'mat_maps'); -load(fullfile(color_map_folder, layers(2).color.map)); -layers(2).color.map = diverging_bwr; - -% layers(2).color.range = [-4 4]; -% layers(2).color.label = '\beta_{listening} - \beta_{baseline} (a.u.)'; - - -% - t-statistics opacity-coded -spmTImage = spm_select('FPList', ffxDir, ['^spmT_' bf.entities.label '.nii$']); -layers(2).opacity.file = spmTImage; - -% layers(2).opacity.range = [0 3]; -% layers(2).opacity.label = '| t |'; - -%% Layer 3 and 4: Contour of ROI -layers(3) = setFields(layers(3), opt.results(iRes).sdConfig.layers{3}, overwrite); - -contour = spm_select('FPList', ffxDir, ['^sub.*' bf.entities.label '.*_mask.nii']); - -layers(3).color.file = contour; -% layers(3).color.map = [0 0 0]; -% layers(3).color.line_width = 2; - -%% Settings -settings = opt.results(iRes).sdConfig.settings; - -% we reuse the details for the SPM montage -% settings.slice.disp_slices = opt.results(1).montage.slices; -% settings.slice.orientation = opt.results(1).montage.orientation; - -settings.fig_specs.title = opt.results(1).name; -%% Display the layers -[settings, p] = sd_display(layers, settings); +color_map_folder = fullfile(returnRootDir(), 'lib', 'brain_colours', 'mat_maps'); + +node = opt.model.bm.Nodes{1}; + +if any(strcmp(node.Level, {'Run', 'Subject'})) + + for iSub = 1:numel(opt.subjects) + + subLabel = opt.subjects{iSub}; + + ffxDir = getFFXdir(subLabel, opt); + load(fullfile(ffxDir, 'SPM.mat')) + + for iRes = 1:numel(node.Model.Software.bidspm.Results) + + opt.results = node.Model.Software.bidspm.Results{iRes}; + + if ~isfield(opt.results, 'montage') || ~opt.results.montage.do + continue + end + + % set defaults + [opt, ~] = checkMontage(opt, iRes, node, struct([]), subLabel); + opt = checkOptions(opt); + opt.results(iRes).montage = setMontage(opt.results(iRes)); + + for i_name =1:numel(opt.results.name) + + if opt.results(iRes).binary + layers = sd_config_layers('init', {'truecolor', 'dual', 'contour'}); + else + layers = sd_config_layers('init', {'truecolor', 'dual'}); + end + + %% Layer 1: Anatomical map + layers(1) = setFields(layers(1), opt.results(iRes).sdConfig.layers{1}, overwrite); + + layers(1).color.file = opt.results(iRes).montage.background{1}; + + hdr = spm_vol(layers(1).color.file); + [max_val, min_val] = slover('volmaxmin', hdr); + layers(1).color.range = [0 max_val]; + + %% Layer 2: Dual-coded layer + + % - contrast estimates color-coded; + layers(2) = setFields(layers(2), opt.results(iRes).sdConfig.layers{2}, overwrite); + + name = opt.results.name{i_name}; + tmp = struct('name', name); + contrastNb = getContrastNb(tmp, opt, SPM); + + % keep track if this is a t test or F test + stat = SPM.xCon(contrastNb).STAT; + + contrastNb = sprintf('%04.0f', contrastNb); + + if strcmp(stat, 'T') + colorFile = spm_select('FPList', ffxDir, ['^con_' contrastNb '.nii$']); + else + colorFile = spm_select('FPList', ffxDir, ['^ess_' contrastNb '.nii$']); + end + layers(2).color.file = colorFile; + + title = strrep(name, '_', ' '); + layers(2).color.label = [title ' (a.u.)']; + + % - statistics opacity-coded + if strcmp(stat, 'T') + opacityFile = spm_select('FPList', ffxDir, ['^spmT_' contrastNb '.nii$']); + + layers(2).opacity.label = '| t |'; + + load(fullfile(color_map_folder, 'diverging_bwr_iso.mat')); + layers(2).color.map = diverging_bwr; + else + opacityFile = spm_select('FPList', ffxDir, ['^spmF_' contrastNb '.nii$']); + + layers(2).opacity.label = 'F'; + + load(fullfile(color_map_folder, '1hot_iso.mat')); + layers(2).color.map = hot; + + hdr = spm_vol(opacityFile); + [max_val, min_val] = slover('volmaxmin', hdr); + layers(2).color.range = [0 max_val]; + + layers(2).opacity.range = [0 5]; + end + layers(2).opacity.file = opacityFile; + + %% Contour + if opt.results(iRes).binary + layers(3) = setFields(layers(3), opt.results(iRes).sdConfig.layers{3}, overwrite); + contour = spm_select('FPList', ffxDir, ['^sub.*' contrastNb '.*_mask.nii']); + layers(3).color.file = contour; + end + + %% Settings + settings = opt.results(iRes).sdConfig.settings; + + % we reuse the details for the SPM montage + settings.slice.disp_slices = opt.results(1).montage.slices; + settings.slice.orientation = opt.results(1).montage.orientation; + + settings.fig_specs.title = title; + + %% Display the layers + [settings, p] = sd_display(layers, settings); + + end + + end + + end +end \ No newline at end of file diff --git a/src/defaults/defaultResultsStructure.m b/src/defaults/defaultResultsStructure.m index aed18634..3a73b988 100644 --- a/src/defaults/defaultResultsStructure.m +++ b/src/defaults/defaultResultsStructure.m @@ -32,17 +32,16 @@ % load(fullfile(color_map_folder, 'diverging_bwr_iso.mat')); layers{2} = struct('color', struct('file', [], ... % con image - 'map', 'diverging_bwr_iso', ... 'range', [-4 4], ... - 'label', '\beta_{listening} - \beta_{baseline} (a.u.)'), ... - 'opacity', struct('file', [], ... % spmT image + 'label', ''), ... + 'opacity', struct('file', [], ... % assume spmT image 'range', [2 3], ... - 'label', '| t |'), ... + 'label', ''), ... 'type', 'dual'); layers{3} = struct('color', struct('file', [], ... % spmT mask thresholded at 0.05 FWD - 'map', [0 0 0], ... - 'line_width', 2)); + 'map', 'w', ... + 'line_width', 1)); result.sdConfig.layers = layers; diff --git a/src/stats/results/renameSpmT.m b/src/stats/results/renameSpmT.m index fc715ddb..55881dd6 100644 --- a/src/stats/results/renameSpmT.m +++ b/src/stats/results/renameSpmT.m @@ -8,7 +8,10 @@ function renameSpmT(result) % % (C) Copyright 2023 bidspm developers - outputFiles = spm_select('FPList', result.dir, '^spmT_[0-9].*_sub-.*nii$'); + prefixes = {'spmT', 'spmF'}; + for i_prefix = 1:numel(prefixes) + + outputFiles = spm_select('FPList', result.dir, ['^' prefixes{i_prefix} '_[0-9].*_sub-.*nii$']); for iFile = 1:size(outputFiles, 1) @@ -22,4 +25,6 @@ function renameSpmT(result) movefile(source, target); end + + end end From 8bdc38396caa5154423838140b6f8dff52c41876 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Tue, 11 Mar 2025 14:41:00 +0100 Subject: [PATCH 07/12] extract transparent plotting --- demos/MoAE/moae_01_bids_app.m | 18 ++-- demos/MoAE/moae_03_slice_display.m | 125 +-------------------- src/stats/results/transparentMontage.m | 144 +++++++++++++++++++++++++ src/workflows/stats/bidsResults.m | 2 + 4 files changed, 156 insertions(+), 133 deletions(-) create mode 100644 src/stats/results/transparentMontage.m diff --git a/demos/MoAE/moae_01_bids_app.m b/demos/MoAE/moae_01_bids_app.m index 9fb9289f..41542151 100644 --- a/demos/MoAE/moae_01_bids_app.m +++ b/demos/MoAE/moae_01_bids_app.m @@ -119,14 +119,14 @@ VERBOSITY = 3; -bidspm(bids_dir, output_dir, 'subject', ... - 'participant_label', {subject_label}, ... - 'action', 'preprocess', ... - 'task', 'auditory', ... - 'ignore', {'unwarp', 'slicetiming'}, ... - 'space', {'IXI549Space'}, ... - 'fwhm', 6, ... - 'verbosity', VERBOSITY); +% bidspm(bids_dir, output_dir, 'subject', ... +% 'participant_label', {subject_label}, ... +% 'action', 'preprocess', ... +% 'task', 'auditory', ... +% 'ignore', {'unwarp', 'slicetiming'}, ... +% 'space', {'IXI549Space'}, ... +% 'fwhm', 6, ... +% 'verbosity', VERBOSITY); % ## Stats @@ -164,7 +164,7 @@ bidspm(bids_dir, output_dir, 'subject', ... 'participant_label', {subject_label}, ... - 'action', 'stats', ... + 'action', 'results', ... 'preproc_dir', preproc_dir, ... 'model_file', model_file, ... 'space', {'IXI549Space'}, ... diff --git a/demos/MoAE/moae_03_slice_display.m b/demos/MoAE/moae_03_slice_display.m index 3f65fc19..54090dcc 100644 --- a/demos/MoAE/moae_03_slice_display.m +++ b/demos/MoAE/moae_03_slice_display.m @@ -20,13 +20,9 @@ opt.pipeline.type = 'stats'; -opt.dir.raw = fullfile(this_dir, 'inputs', 'raw'); opt.dir.derivatives = fullfile(this_dir, 'outputs', 'derivatives'); opt.dir.preproc = fullfile(opt.dir.derivatives, 'bidspm-preproc'); -opt.dir.roi = fullfile(opt.dir.derivatives, 'bidspm-roi'); -opt.dir.stats = fullfile(opt.dir.derivatives, 'bidspm-stats'); - opt.model.file = fullfile(this_dir, 'models', 'model-MoAE_smdl.json'); opt.subjects = {'01'}; @@ -34,123 +30,4 @@ % read the model opt = checkOptions(opt); -% TODO loop over noce and subjects - -overwrite = true; - -color_map_folder = fullfile(returnRootDir(), 'lib', 'brain_colours', 'mat_maps'); - -node = opt.model.bm.Nodes{1}; - -if any(strcmp(node.Level, {'Run', 'Subject'})) - - for iSub = 1:numel(opt.subjects) - - subLabel = opt.subjects{iSub}; - - ffxDir = getFFXdir(subLabel, opt); - load(fullfile(ffxDir, 'SPM.mat')) - - for iRes = 1:numel(node.Model.Software.bidspm.Results) - - opt.results = node.Model.Software.bidspm.Results{iRes}; - - if ~isfield(opt.results, 'montage') || ~opt.results.montage.do - continue - end - - % set defaults - [opt, ~] = checkMontage(opt, iRes, node, struct([]), subLabel); - opt = checkOptions(opt); - opt.results(iRes).montage = setMontage(opt.results(iRes)); - - for i_name =1:numel(opt.results.name) - - if opt.results(iRes).binary - layers = sd_config_layers('init', {'truecolor', 'dual', 'contour'}); - else - layers = sd_config_layers('init', {'truecolor', 'dual'}); - end - - %% Layer 1: Anatomical map - layers(1) = setFields(layers(1), opt.results(iRes).sdConfig.layers{1}, overwrite); - - layers(1).color.file = opt.results(iRes).montage.background{1}; - - hdr = spm_vol(layers(1).color.file); - [max_val, min_val] = slover('volmaxmin', hdr); - layers(1).color.range = [0 max_val]; - - %% Layer 2: Dual-coded layer - - % - contrast estimates color-coded; - layers(2) = setFields(layers(2), opt.results(iRes).sdConfig.layers{2}, overwrite); - - name = opt.results.name{i_name}; - tmp = struct('name', name); - contrastNb = getContrastNb(tmp, opt, SPM); - - % keep track if this is a t test or F test - stat = SPM.xCon(contrastNb).STAT; - - contrastNb = sprintf('%04.0f', contrastNb); - - if strcmp(stat, 'T') - colorFile = spm_select('FPList', ffxDir, ['^con_' contrastNb '.nii$']); - else - colorFile = spm_select('FPList', ffxDir, ['^ess_' contrastNb '.nii$']); - end - layers(2).color.file = colorFile; - - title = strrep(name, '_', ' '); - layers(2).color.label = [title ' (a.u.)']; - - % - statistics opacity-coded - if strcmp(stat, 'T') - opacityFile = spm_select('FPList', ffxDir, ['^spmT_' contrastNb '.nii$']); - - layers(2).opacity.label = '| t |'; - - load(fullfile(color_map_folder, 'diverging_bwr_iso.mat')); - layers(2).color.map = diverging_bwr; - else - opacityFile = spm_select('FPList', ffxDir, ['^spmF_' contrastNb '.nii$']); - - layers(2).opacity.label = 'F'; - - load(fullfile(color_map_folder, '1hot_iso.mat')); - layers(2).color.map = hot; - - hdr = spm_vol(opacityFile); - [max_val, min_val] = slover('volmaxmin', hdr); - layers(2).color.range = [0 max_val]; - - layers(2).opacity.range = [0 5]; - end - layers(2).opacity.file = opacityFile; - - %% Contour - if opt.results(iRes).binary - layers(3) = setFields(layers(3), opt.results(iRes).sdConfig.layers{3}, overwrite); - contour = spm_select('FPList', ffxDir, ['^sub.*' contrastNb '.*_mask.nii']); - layers(3).color.file = contour; - end - - %% Settings - settings = opt.results(iRes).sdConfig.settings; - - % we reuse the details for the SPM montage - settings.slice.disp_slices = opt.results(1).montage.slices; - settings.slice.orientation = opt.results(1).montage.orientation; - - settings.fig_specs.title = title; - - %% Display the layers - [settings, p] = sd_display(layers, settings); - - end - - end - - end -end \ No newline at end of file +transparentMontage(opt) diff --git a/src/stats/results/transparentMontage.m b/src/stats/results/transparentMontage.m new file mode 100644 index 00000000..1e25e94a --- /dev/null +++ b/src/stats/results/transparentMontage.m @@ -0,0 +1,144 @@ + +function transparentMontage(opt) + % + % Generate montage with transparent plotting + % + % USAGE:: + % + % transparentMontage(opt) + % + % + + % (C) Copyright 2025 bidspm developers + + overwrite = true; + + color_map_folder = fullfile(returnRootDir(), 'lib', 'brain_colours', 'mat_maps'); + + for i_node = 1:numel(opt.model.bm.Nodes) + + node = opt.model.bm.Nodes{i_node}; + + if any(strcmp(node.Level, {'Run', 'Subject'})) + + for iSub = 1:numel(opt.subjects) + + subLabel = opt.subjects{iSub}; + + ffxDir = getFFXdir(subLabel, opt); + load(fullfile(ffxDir, 'SPM.mat')) + + for iRes = 1:numel(node.Model.Software.bidspm.Results) + + opt.results = node.Model.Software.bidspm.Results{iRes}; + + if ~isfield(opt.results, 'montage') || ~opt.results.montage.do + continue + end + + % set defaults + [opt, ~] = checkMontage(opt, iRes, node, struct([]), subLabel); + opt = checkOptions(opt); + opt.results(iRes).montage = setMontage(opt.results(iRes)); + + for i_name =1:numel(opt.results.name) + + if opt.results(iRes).binary + layers = sd_config_layers('init', {'truecolor', 'dual', 'contour'}); + else + layers = sd_config_layers('init', {'truecolor', 'dual'}); + end + + %% Layer 1: Anatomical map + layers(1) = setFields(layers(1), opt.results(iRes).sdConfig.layers{1}, overwrite); + + layers(1).color.file = opt.results(iRes).montage.background{1}; + + hdr = spm_vol(layers(1).color.file); + [max_val, ~] = slover('volmaxmin', hdr); + layers(1).color.range = [0 max_val]; + + %% Layer 2: Dual-coded layer + + % - contrast estimates color-coded; + layers(2) = setFields(layers(2), opt.results(iRes).sdConfig.layers{2}, overwrite); + + name = opt.results.name{i_name}; + tmp = struct('name', name); + contrastNb = getContrastNb(tmp, opt, SPM); + + % keep track if this is a t test or F test + stat = SPM.xCon(contrastNb).STAT; + + contrastNb = sprintf('%04.0f', contrastNb); + + if strcmp(stat, 'T') + colorFile = spm_select('FPList', ffxDir, ['^con_' contrastNb '.nii$']); + else + colorFile = spm_select('FPList', ffxDir, ['^ess_' contrastNb '.nii$']); + end + layers(2).color.file = colorFile; + + title = strrep(name, '_', ' '); + layers(2).color.label = [title ' (a.u.)']; + + % - statistics opacity-coded + if strcmp(stat, 'T') + opacityFile = spm_select('FPList', ffxDir, ['^spmT_' contrastNb '.nii$']); + + layers(2).opacity.label = '| t |'; + + load(fullfile(color_map_folder, 'diverging_bwr_iso.mat')); %#ok<*LOAD> + layers(2).color.map = diverging_bwr; + else + opacityFile = spm_select('FPList', ffxDir, ['^spmF_' contrastNb '.nii$']); + + layers(2).opacity.label = 'F'; + + load(fullfile(color_map_folder, '1hot_iso.mat')); + layers(2).color.map = hot; + + hdr = spm_vol(opacityFile); + [max_val, ~] = slover('volmaxmin', hdr); + layers(2).color.range = [0 max_val]; + + layers(2).opacity.range = [0 5]; + end + layers(2).opacity.file = opacityFile; + + %% Contour + if opt.results(iRes).binary + layers(3) = setFields(layers(3), opt.results(iRes).sdConfig.layers{3}, overwrite); + contour = spm_select('FPList', ffxDir, ['^sub.*' contrastNb '.*_mask.nii']); + layers(3).color.file = contour; + end + + %% Settings + settings = opt.results(iRes).sdConfig.settings; + + % we reuse the details for the SPM montage + settings.slice.disp_slices = opt.results(1).montage.slices; + settings.slice.orientation = opt.results(1).montage.orientation; + + settings.fig_specs.title = title; + + %% Display the layers + [~, ~, h_figure] = sd_display(layers, settings); + + outputFile = fullfile(ffxDir, [contrastNb '_' name '.png']); + print(h_figure, outputFile, '-dpng'); + close(h_figure) + + % TODO + % rename file + + + end + + end + + end + end + end + +end diff --git a/src/workflows/stats/bidsResults.m b/src/workflows/stats/bidsResults.m index 9f85f6bb..788a693e 100644 --- a/src/workflows/stats/bidsResults.m +++ b/src/workflows/stats/bidsResults.m @@ -271,6 +271,8 @@ cleanUpWorkflow(opt); + transparentMontage(opt) + end function [opt, listNodeLevels] = keepRequestedNodes(opt, nodeName, analysisLevel) From 8663a48762be7cd577e9bca52ade664aeb9b259f Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Tue, 11 Mar 2025 14:53:48 +0100 Subject: [PATCH 08/12] ref --- demos/MoAE/moae_03_slice_display.m | 4 +- src/stats/results/renameSpmT.m | 22 +-- src/stats/results/transparentMontage.m | 258 +++++++++++++------------ src/workflows/stats/bidsResults.m | 2 +- 4 files changed, 143 insertions(+), 143 deletions(-) diff --git a/demos/MoAE/moae_03_slice_display.m b/demos/MoAE/moae_03_slice_display.m index 54090dcc..478d82c7 100644 --- a/demos/MoAE/moae_03_slice_display.m +++ b/demos/MoAE/moae_03_slice_display.m @@ -28,6 +28,4 @@ opt.subjects = {'01'}; % read the model -opt = checkOptions(opt); - -transparentMontage(opt) +opt = chec \ No newline at end of file diff --git a/src/stats/results/renameSpmT.m b/src/stats/results/renameSpmT.m index 55881dd6..9e771681 100644 --- a/src/stats/results/renameSpmT.m +++ b/src/stats/results/renameSpmT.m @@ -10,21 +10,21 @@ function renameSpmT(result) % (C) Copyright 2023 bidspm developers prefixes = {'spmT', 'spmF'}; for i_prefix = 1:numel(prefixes) - - outputFiles = spm_select('FPList', result.dir, ['^' prefixes{i_prefix} '_[0-9].*_sub-.*nii$']); - for iFile = 1:size(outputFiles, 1) + outputFiles = spm_select('FPList', result.dir, ['^' prefixes{i_prefix} '_[0-9].*_sub-.*nii$']); - source = deblank(outputFiles(iFile, :)); + for iFile = 1:size(outputFiles, 1) - basename = spm_file(source, 'basename'); - split = strfind(basename, '_sub'); - bf = bids.File(basename(split + 1:end)); + source = deblank(outputFiles(iFile, :)); - target = spm_file(source, 'basename', bf.filename); + basename = spm_file(source, 'basename'); + split = strfind(basename, '_sub'); + bf = bids.File(basename(split + 1:end)); + + target = spm_file(source, 'basename', bf.filename); + + movefile(source, target); + end - movefile(source, target); - end - end end diff --git a/src/stats/results/transparentMontage.m b/src/stats/results/transparentMontage.m index 1e25e94a..ee6ae7c1 100644 --- a/src/stats/results/transparentMontage.m +++ b/src/stats/results/transparentMontage.m @@ -1,4 +1,3 @@ - function transparentMontage(opt) % % Generate montage with transparent plotting @@ -11,134 +10,137 @@ function transparentMontage(opt) % (C) Copyright 2025 bidspm developers - overwrite = true; - - color_map_folder = fullfile(returnRootDir(), 'lib', 'brain_colours', 'mat_maps'); - - for i_node = 1:numel(opt.model.bm.Nodes) - - node = opt.model.bm.Nodes{i_node}; - + for i_node = 1:numel(opt.model.bm.Nodes) + + node = opt.model.bm.Nodes{i_node}; + if any(strcmp(node.Level, {'Run', 'Subject'})) - - for iSub = 1:numel(opt.subjects) - - subLabel = opt.subjects{iSub}; - - ffxDir = getFFXdir(subLabel, opt); - load(fullfile(ffxDir, 'SPM.mat')) - - for iRes = 1:numel(node.Model.Software.bidspm.Results) - - opt.results = node.Model.Software.bidspm.Results{iRes}; - - if ~isfield(opt.results, 'montage') || ~opt.results.montage.do - continue - end - - % set defaults - [opt, ~] = checkMontage(opt, iRes, node, struct([]), subLabel); - opt = checkOptions(opt); - opt.results(iRes).montage = setMontage(opt.results(iRes)); - - for i_name =1:numel(opt.results.name) - - if opt.results(iRes).binary - layers = sd_config_layers('init', {'truecolor', 'dual', 'contour'}); - else - layers = sd_config_layers('init', {'truecolor', 'dual'}); - end - - %% Layer 1: Anatomical map - layers(1) = setFields(layers(1), opt.results(iRes).sdConfig.layers{1}, overwrite); - - layers(1).color.file = opt.results(iRes).montage.background{1}; - - hdr = spm_vol(layers(1).color.file); - [max_val, ~] = slover('volmaxmin', hdr); - layers(1).color.range = [0 max_val]; - - %% Layer 2: Dual-coded layer - - % - contrast estimates color-coded; - layers(2) = setFields(layers(2), opt.results(iRes).sdConfig.layers{2}, overwrite); - - name = opt.results.name{i_name}; - tmp = struct('name', name); - contrastNb = getContrastNb(tmp, opt, SPM); - - % keep track if this is a t test or F test - stat = SPM.xCon(contrastNb).STAT; - - contrastNb = sprintf('%04.0f', contrastNb); - - if strcmp(stat, 'T') - colorFile = spm_select('FPList', ffxDir, ['^con_' contrastNb '.nii$']); - else - colorFile = spm_select('FPList', ffxDir, ['^ess_' contrastNb '.nii$']); - end - layers(2).color.file = colorFile; - - title = strrep(name, '_', ' '); - layers(2).color.label = [title ' (a.u.)']; - - % - statistics opacity-coded - if strcmp(stat, 'T') - opacityFile = spm_select('FPList', ffxDir, ['^spmT_' contrastNb '.nii$']); - - layers(2).opacity.label = '| t |'; - - load(fullfile(color_map_folder, 'diverging_bwr_iso.mat')); %#ok<*LOAD> - layers(2).color.map = diverging_bwr; - else - opacityFile = spm_select('FPList', ffxDir, ['^spmF_' contrastNb '.nii$']); - - layers(2).opacity.label = 'F'; - - load(fullfile(color_map_folder, '1hot_iso.mat')); - layers(2).color.map = hot; - - hdr = spm_vol(opacityFile); - [max_val, ~] = slover('volmaxmin', hdr); - layers(2).color.range = [0 max_val]; - - layers(2).opacity.range = [0 5]; - end - layers(2).opacity.file = opacityFile; - - %% Contour - if opt.results(iRes).binary - layers(3) = setFields(layers(3), opt.results(iRes).sdConfig.layers{3}, overwrite); - contour = spm_select('FPList', ffxDir, ['^sub.*' contrastNb '.*_mask.nii']); - layers(3).color.file = contour; - end - - %% Settings - settings = opt.results(iRes).sdConfig.settings; - - % we reuse the details for the SPM montage - settings.slice.disp_slices = opt.results(1).montage.slices; - settings.slice.orientation = opt.results(1).montage.orientation; - - settings.fig_specs.title = title; - - %% Display the layers - [~, ~, h_figure] = sd_display(layers, settings); - - outputFile = fullfile(ffxDir, [contrastNb '_' name '.png']); - print(h_figure, outputFile, '-dpng'); - close(h_figure) - - % TODO - % rename file - - - end - - end - + + for iSub = 1:numel(opt.subjects) + + subLabel = opt.subjects{iSub}; + + ffxDir = getFFXdir(subLabel, opt); + load(fullfile(ffxDir, 'SPM.mat')); + + for iRes = 1:numel(node.Model.Software.bidspm.Results) + + opt.results = node.Model.Software.bidspm.Results{iRes}; + + if ~isfield(opt.results, 'montage') || ~opt.results.montage.do + continue + end + + % set defaults + [opt, ~] = checkMontage(opt, iRes, node, struct([]), subLabel); + opt = checkOptions(opt); + opt.results(iRes).montage = setMontage(opt.results(iRes)); + + for iName = 1:numel(opt.results.name) + + plotTransparentMontage(opt, SPM, subLabel, iRes, iName); + + end + end + + end + end - end - + + end + +end + +function plotTransparentMontage(opt, SPM, subLabel, iRes, iName) + + overwrite = true; + + color_map_folder = fullfile(returnRootDir(), 'lib', 'brain_colours', 'mat_maps'); + + ffxDir = getFFXdir(subLabel, opt); + + if opt.results(iRes).binary + layers = sd_config_layers('init', {'truecolor', 'dual', 'contour'}); + else + layers = sd_config_layers('init', {'truecolor', 'dual'}); + end + + %% Layer 1: Anatomical map + layers(1) = setFields(layers(1), opt.results(iRes).sdConfig.layers{1}, overwrite); + + layers(1).color.file = opt.results(iRes).montage.background{1}; + + hdr = spm_vol(layers(1).color.file); + [max_val, ~] = slover('volmaxmin', hdr); + layers(1).color.range = [0 max_val]; + + %% Layer 2: Dual-coded layer + + % - contrast estimates color-coded; + layers(2) = setFields(layers(2), opt.results(iRes).sdConfig.layers{2}, overwrite); + + name = opt.results.name{iName}; + tmp = struct('name', name); + contrastNb = getContrastNb(tmp, opt, SPM); + % keep track if this is a t test or F test + stat = SPM.xCon(contrastNb).STAT; + contrastNb = sprintf('%04.0f', contrastNb); + + % - statistics opacity-coded + if strcmp(stat, 'T') + colorFile = spm_select('FPList', ffxDir, ['^con_' contrastNb '.nii$']); + opacityFile = spm_select('FPList', ffxDir, ['^spmT_' contrastNb '.nii$']); + + layers(2).opacity.label = '| t |'; + + load(fullfile(color_map_folder, 'diverging_bwr_iso.mat')); %#ok<*LOAD> + layers(2).color.map = diverging_bwr; + else + colorFile = spm_select('FPList', ffxDir, ['^ess_' contrastNb '.nii$']); + opacityFile = spm_select('FPList', ffxDir, ['^spmF_' contrastNb '.nii$']); + + layers(2).opacity.label = 'F'; + + load(fullfile(color_map_folder, '1hot_iso.mat')); + layers(2).color.map = hot; + + hdr = spm_vol(opacityFile); + [max_val, ~] = slover('volmaxmin', hdr); + layers(2).color.range = [0 max_val]; + + layers(2).opacity.range = [0 5]; + end + layers(2).color.file = colorFile; + layers(2).opacity.file = opacityFile; + + title = strrep(name, '_', ' '); + layers(2).color.label = [title ' (a.u.)']; + + %% Contour + if opt.results(iRes).binary + layers(3) = setFields(layers(3), opt.results(iRes).sdConfig.layers{3}, overwrite); + contour = spm_select('FPList', ffxDir, ['^sub.*' contrastNb '.*_mask.nii']); + layers(3).color.file = contour; + end + + %% Settings + settings = opt.results(iRes).sdConfig.settings; + + % we reuse the details for the SPM montage + settings.slice.disp_slices = opt.results(1).montage.slices; + settings.slice.orientation = opt.results(1).montage.orientation; + + settings.fig_specs.title = title; + + %% Display the layers + [~, ~, h_figure] = sd_display(layers, settings); + + outputFile = fullfile(ffxDir, [contrastNb '_' name '.png']); + print(h_figure, outputFile, '-dpng'); + close(h_figure); + + % TODO + % rename file + end diff --git a/src/workflows/stats/bidsResults.m b/src/workflows/stats/bidsResults.m index 788a693e..8ac56e50 100644 --- a/src/workflows/stats/bidsResults.m +++ b/src/workflows/stats/bidsResults.m @@ -271,7 +271,7 @@ cleanUpWorkflow(opt); - transparentMontage(opt) + transparentMontage(opt); end From 0a388b3eb4dffcf565afd64b8a9933c501e9c09b Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Tue, 11 Mar 2025 14:54:39 +0100 Subject: [PATCH 09/12] fix --- demos/MoAE/moae_03_slice_display.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/demos/MoAE/moae_03_slice_display.m b/demos/MoAE/moae_03_slice_display.m index 478d82c7..79f67205 100644 --- a/demos/MoAE/moae_03_slice_display.m +++ b/demos/MoAE/moae_03_slice_display.m @@ -28,4 +28,6 @@ opt.subjects = {'01'}; % read the model -opt = chec \ No newline at end of file +opt = checkOptions(opt); + +transparentMontage(opt); From 32b9ca16ea838c53085d47db86e77a8b56c37644 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Tue, 11 Mar 2025 16:55:25 +0100 Subject: [PATCH 10/12] fix --- lib/slice_display | 2 +- src/bids_model/BidsModel.m | 1 + src/stats/results/transparentMontage.m | 79 ++++++++++++++++++-------- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/lib/slice_display b/lib/slice_display index 4326779c..f6f1ce42 160000 --- a/lib/slice_display +++ b/lib/slice_display @@ -1 +1 @@ -Subproject commit 4326779c8e9d7681e0b13827196aad64c801e170 +Subproject commit f6f1ce42f56ae7bc40e697a1083bc6514a631a9f diff --git a/src/bids_model/BidsModel.m b/src/bids_model/BidsModel.m index 0c49baea..65e6ad43 100644 --- a/src/bids_model/BidsModel.m +++ b/src/bids_model/BidsModel.m @@ -312,6 +312,7 @@ end function results = getResults(obj) + % return results from all nodes results = struct([]); idx = 1; diff --git a/src/stats/results/transparentMontage.m b/src/stats/results/transparentMontage.m index ee6ae7c1..d7fb413f 100644 --- a/src/stats/results/transparentMontage.m +++ b/src/stats/results/transparentMontage.m @@ -1,18 +1,58 @@ function transparentMontage(opt) % - % Generate montage with transparent plotting + % Generate montage with transparent plotting using slice_display toolbox. % % USAGE:: % % transparentMontage(opt) % + % EXAMPLE:: + % + % opt.pipeline.type = 'stats'; + % + % opt.dir.derivatives = fullfile(this_dir, 'outputs', 'derivatives'); + % opt.dir.preproc = fullfile(opt.dir.derivatives, 'bidspm-preproc'); + % opt.model.file = fullfile(this_dir, 'models', 'model-MoAE_smdl.json'); + % + % opt.subjects = {'01'}; + % + % opt = checkOptions(opt); + % + % transparentMontage(opt); % % (C) Copyright 2025 bidspm developers - for i_node = 1:numel(opt.model.bm.Nodes) + bm = opt.model.bm; + + modelResults = bm.getResults(); + if ~isempty(modelResults) + opt.results = modelResults; + end - node = opt.model.bm.Nodes{i_node}; + % loop through the steps to compute for each contrast mentioned for each node + for iRes = 1:length(opt.results) + + node = bm.get_nodes('Name', opt.results(iRes).nodeName); + + if isempty(node) + + id = 'unknownModelNode'; + msg = sprintf('no Node named %s in model\n %s.', ... + opt.results(iRes).nodeName, ... + opt.model.file); + logger('WARNING', msg, 'id', id, 'filename', mfilename(), 'options', opt); + continue + end + + opt.results(iRes); + + if ~isfield(opt.results(iRes), 'montage') || ~opt.results(iRes).montage.do + continue + end + + msg = sprintf('\n PROCESSING NODE: %s\n', node.Name); + logger('INFO', msg, 'options', opt, 'filename', mfilename()); if any(strcmp(node.Level, {'Run', 'Subject'})) @@ -23,24 +63,15 @@ function transparentMontage(opt) ffxDir = getFFXdir(subLabel, opt); load(fullfile(ffxDir, 'SPM.mat')); - for iRes = 1:numel(node.Model.Software.bidspm.Results) - - opt.results = node.Model.Software.bidspm.Results{iRes}; - - if ~isfield(opt.results, 'montage') || ~opt.results.montage.do - continue - end - - % set defaults - [opt, ~] = checkMontage(opt, iRes, node, struct([]), subLabel); - opt = checkOptions(opt); - opt.results(iRes).montage = setMontage(opt.results(iRes)); - - for iName = 1:numel(opt.results.name) + % set defaults + % TODO check plotting is done on the right background + [optThisSubject, ~] = checkMontage(opt, iRes, node, struct([]), subLabel); + optThisSubject = checkOptions(optThisSubject); + optThisSubject.results(iRes).montage = setMontage(optThisSubject.results(iRes)); - plotTransparentMontage(opt, SPM, subLabel, iRes, iName); + for iName = 1:numel(optThisSubject.results(iRes).name) - end + plotTransparentMontage(optThisSubject, SPM, subLabel, iRes, iName); end @@ -53,13 +84,12 @@ function transparentMontage(opt) end function plotTransparentMontage(opt, SPM, subLabel, iRes, iName) - + % Generate a single transparent plot. + % overwrite = true; color_map_folder = fullfile(returnRootDir(), 'lib', 'brain_colours', 'mat_maps'); - ffxDir = getFFXdir(subLabel, opt); - if opt.results(iRes).binary layers = sd_config_layers('init', {'truecolor', 'dual', 'contour'}); else @@ -80,7 +110,7 @@ function plotTransparentMontage(opt, SPM, subLabel, iRes, iName) % - contrast estimates color-coded; layers(2) = setFields(layers(2), opt.results(iRes).sdConfig.layers{2}, overwrite); - name = opt.results.name{iName}; + name = opt.results(iRes).name{iName}; tmp = struct('name', name); contrastNb = getContrastNb(tmp, opt, SPM); % keep track if this is a t test or F test @@ -88,6 +118,7 @@ function plotTransparentMontage(opt, SPM, subLabel, iRes, iName) contrastNb = sprintf('%04.0f', contrastNb); % - statistics opacity-coded + ffxDir = getFFXdir(subLabel, opt); if strcmp(stat, 'T') colorFile = spm_select('FPList', ffxDir, ['^con_' contrastNb '.nii$']); opacityFile = spm_select('FPList', ffxDir, ['^spmT_' contrastNb '.nii$']); @@ -96,6 +127,7 @@ function plotTransparentMontage(opt, SPM, subLabel, iRes, iName) load(fullfile(color_map_folder, 'diverging_bwr_iso.mat')); %#ok<*LOAD> layers(2).color.map = diverging_bwr; + else colorFile = spm_select('FPList', ffxDir, ['^ess_' contrastNb '.nii$']); opacityFile = spm_select('FPList', ffxDir, ['^spmF_' contrastNb '.nii$']); @@ -134,6 +166,7 @@ function plotTransparentMontage(opt, SPM, subLabel, iRes, iName) settings.fig_specs.title = title; %% Display the layers + settings.slice.zmm; [~, ~, h_figure] = sd_display(layers, settings); outputFile = fullfile(ffxDir, [contrastNb '_' name '.png']); From 95348ff00ff98d9f30fadf928bc3731fd4a40e50 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Tue, 11 Mar 2025 16:58:07 +0100 Subject: [PATCH 11/12] simplify demo --- demos/MoAE/moae_03_slice_display.m | 1 - 1 file changed, 1 deletion(-) diff --git a/demos/MoAE/moae_03_slice_display.m b/demos/MoAE/moae_03_slice_display.m index 79f67205..de4847d0 100644 --- a/demos/MoAE/moae_03_slice_display.m +++ b/demos/MoAE/moae_03_slice_display.m @@ -27,7 +27,6 @@ opt.subjects = {'01'}; -% read the model opt = checkOptions(opt); transparentMontage(opt); From 6ecc760bf4e329ca465a411fbe5e5757c49ba3a5 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Tue, 11 Mar 2025 17:07:28 +0100 Subject: [PATCH 12/12] revert --- demos/MoAE/moae_01_bids_app.m | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/demos/MoAE/moae_01_bids_app.m b/demos/MoAE/moae_01_bids_app.m index 41542151..9fb9289f 100644 --- a/demos/MoAE/moae_01_bids_app.m +++ b/demos/MoAE/moae_01_bids_app.m @@ -119,14 +119,14 @@ VERBOSITY = 3; -% bidspm(bids_dir, output_dir, 'subject', ... -% 'participant_label', {subject_label}, ... -% 'action', 'preprocess', ... -% 'task', 'auditory', ... -% 'ignore', {'unwarp', 'slicetiming'}, ... -% 'space', {'IXI549Space'}, ... -% 'fwhm', 6, ... -% 'verbosity', VERBOSITY); +bidspm(bids_dir, output_dir, 'subject', ... + 'participant_label', {subject_label}, ... + 'action', 'preprocess', ... + 'task', 'auditory', ... + 'ignore', {'unwarp', 'slicetiming'}, ... + 'space', {'IXI549Space'}, ... + 'fwhm', 6, ... + 'verbosity', VERBOSITY); % ## Stats @@ -164,7 +164,7 @@ bidspm(bids_dir, output_dir, 'subject', ... 'participant_label', {subject_label}, ... - 'action', 'results', ... + 'action', 'stats', ... 'preproc_dir', preproc_dir, ... 'model_file', model_file, ... 'space', {'IXI549Space'}, ...