From 1d91b3ed351c80c72647c31806aad41bfe61ffbc Mon Sep 17 00:00:00 2001 From: ftadel Date: Thu, 14 May 2020 16:18:33 +0200 Subject: [PATCH] CAT12: Updated version 12.7 + additional options --- toolbox/io/import_anatomy_cat.m | 41 ++--- .../process/functions/process_segment_cat12.m | 140 +++++++++++++----- toolbox/process/panel_process_select.m | 6 +- 3 files changed, 132 insertions(+), 55 deletions(-) diff --git a/toolbox/io/import_anatomy_cat.m b/toolbox/io/import_anatomy_cat.m index f71f1f06a..598fb396f 100644 --- a/toolbox/io/import_anatomy_cat.m +++ b/toolbox/io/import_anatomy_cat.m @@ -330,8 +330,8 @@ end end -% Left FSAverage -if ~isempty(FsAvgLhFile) +% Left FSAverage (only if the spheres are available) +if ~isempty(FsAvgLhFile) && ~isempty(TessLsphFile) % Import file [iAvgLh, BstFsAvgLhFile, nVertOrigAvgL] = import_surfaces(iSubject, FsAvgLhFile, 'GII-WORLD', 0); BstFsAvgLhFile = BstFsAvgLhFile{1}; @@ -348,8 +348,8 @@ errorMsg = [errorMsg err]; end end -% Right FSAverage -if ~isempty(FsAvgRhFile) +% Right FSAverage (only if the spheres are available) +if ~isempty(FsAvgRhFile) && ~isempty(TessRsphFile) % Import file [iAvgRh, BstFsAvgRhFile, nVertOrigAvgR] = import_surfaces(iSubject, FsAvgRhFile, 'GII-WORLD', 0); BstFsAvgRhFile = BstFsAvgRhFile{1}; @@ -367,8 +367,8 @@ end end -% Left FSAverage 32k -if ~isempty(Fs32kLhFile) +% Left FSAverage 32k (only if the spheres are available) +if ~isempty(Fs32kLhFile) && ~isempty(TessLsphFile) % Import file [i32kLh, BstFs32kLhFile, nVertOrig32kL] = import_surfaces(iSubject, Fs32kLhFile, 'GII-WORLD', 0); BstFs32kLhFile = BstFs32kLhFile{1}; @@ -385,8 +385,8 @@ errorMsg = [errorMsg err]; end end -% Right FSAverage 32k -if ~isempty(Fs32kRhFile) +% Right FSAverage 32k (only if the spheres are available) +if ~isempty(Fs32kRhFile) && ~isempty(TessRsphFile) % Import file [i32kRh, BstFs32kRhFile, nVertOrig32kR] = import_surfaces(iSubject, Fs32kRhFile, 'GII-WORLD', 0); BstFs32kRhFile = BstFs32kRhFile{1}; @@ -417,17 +417,20 @@ %% ===== PROJECT ATLASES ===== rmFiles = {}; -% Project FSAverage atlases -if ~isempty(FsAvgLhFile) && ~isempty(FsAvgRhFile) - bst_project_scouts(BstFsAvgLhFile, BstTessLhFile, [], 1); - bst_project_scouts(BstFsAvgRhFile, BstTessRhFile, [], 1); - rmFiles = cat(2, rmFiles, {BstFsAvgLhFile, BstFsAvgRhFile}); -end -% Project FSAverage 32k atlases -if ~isempty(Fs32kLhFile) && ~isempty(Fs32kRhFile) - bst_project_scouts(BstFs32kLhFile, BstTessLhFile, [], 1); - bst_project_scouts(BstFs32kRhFile, BstTessRhFile, [], 1); - rmFiles = cat(2, rmFiles, {BstFs32kLhFile, BstFs32kRhFile}); +% If the registered spheres are available +if ~isempty(TessLsphFile) && ~isempty(TessRsphFile) + % Project FSAverage atlases + if ~isempty(FsAvgLhFile) && ~isempty(FsAvgRhFile) + bst_project_scouts(BstFsAvgLhFile, BstTessLhFile, [], 1); + bst_project_scouts(BstFsAvgRhFile, BstTessRhFile, [], 1); + rmFiles = cat(2, rmFiles, {BstFsAvgLhFile, BstFsAvgRhFile}); + end + % Project FSAverage 32k atlases + if ~isempty(Fs32kLhFile) && ~isempty(Fs32kRhFile) + bst_project_scouts(BstFs32kLhFile, BstTessLhFile, [], 1); + bst_project_scouts(BstFs32kRhFile, BstTessRhFile, [], 1); + rmFiles = cat(2, rmFiles, {BstFs32kLhFile, BstFs32kRhFile}); + end end diff --git a/toolbox/process/functions/process_segment_cat12.m b/toolbox/process/functions/process_segment_cat12.m index c84d47fd8..4ecb7b4d9 100644 --- a/toolbox/process/functions/process_segment_cat12.m +++ b/toolbox/process/functions/process_segment_cat12.m @@ -2,7 +2,8 @@ % PROCESS_SEGMENT_CAT12: Run the segmentation of a T1 MRI with SPM12/CAT12. % % USAGE: OutputFiles = process_segment_cat12('Run', sProcess, sInputs) -% [isOk, errMsg] = process_segment_cat12('Compute', iSubject, iAnatomy=[default]) +% [isOk, errMsg] = process_segment_cat12('Compute', iSubject, iAnatomy=[default], nVertices, isSphReg, isExtraMaps, isInteractive) +% process_segment_cat12('ComputeInteractive', iSubject, iAnatomy) % @============================================================================= % This function is part of the Brainstorm software: @@ -22,7 +23,7 @@ % For more information type "brainstorm license" at command prompt. % =============================================================================@ % -% Authors: Francois Tadel, 2019 +% Authors: Francois Tadel, 2019-2020 eval(macro_method); end @@ -49,6 +50,28 @@ sProcess.options.nvertices.Comment = 'Number of vertices (cortex): '; sProcess.options.nvertices.Type = 'value'; sProcess.options.nvertices.Value = {15000, '', 0}; + % Option: TPM atlas + SelectOptions = {... + '', ... % Filename + 'Nifti1', ... % FileFormat + 'open', ... % Dialog type: {open,save} + 'Select TPM atlas...', ... % Window title + 'ImportAnat', ... % LastUsedDir: {ImportData,ImportChannel,ImportAnat,ExportChannel,ExportData,ExportAnat,ExportProtocol,ExportImage,ExportScript} + 'single', ... % Selection mode: {single,multiple} + 'files', ... % Selection mode: {files,dirs,files_and_dirs} + {{'.nii','.gz'}, 'MRI: NIfTI-1 (*.nii;*.nii.gz)', 'Nifti1'}, ... % Get all the available file formats + 'MriIn'}; % DefaultFormats: {ChannelIn,DataIn,DipolesIn,EventsIn,MriIn,NoiseCovIn,ResultsIn,SspIn,SurfaceIn,TimefreqIn + sProcess.options.tpmnii.Comment = 'TPM atlas: '; + sProcess.options.tpmnii.Type = 'filename'; + sProcess.options.tpmnii.Value = SelectOptions; + % Option: Spherical registration + sProcess.options.sphreg.Comment = 'Use spherical registration
Required for atlases, group analysis and thickness maps'; + sProcess.options.sphreg.Type = 'checkbox'; + sProcess.options.sphreg.Value = 1; + % Option: Import thickness map + sProcess.options.thickness.Comment = 'Import cortical thickness map'; + sProcess.options.thickness.Type = 'checkbox'; + sProcess.options.thickness.Value = 0; end @@ -67,6 +90,25 @@ bst_report('Error', sProcess, [], 'Invalid number of vertices.'); return end + % Spherical registration? + if isfield(sProcess.options, 'sphreg') && isfield(sProcess.options.sphreg, 'Value') && ~isempty(sProcess.options.sphreg.Value) + isSphReg = sProcess.options.sphreg.Value; + else + isSphReg = 1; + end + % TPM atlas + if isfield(sProcess.options, 'tpmnii') && isfield(sProcess.options.tpmnii, 'Value') && ~isempty(sProcess.options.tpmnii.Value) && ~isempty(sProcess.options.tpmnii.Value{1}) + TpmNii = sProcess.options.tpmnii.Value{1}; + else + TpmNii = bst_get('SpmTpmAtlas'); + end + % Thickness maps + if isfield(sProcess.options, 'thickness') && isfield(sProcess.options.thickness, 'Value') && ~isempty(sProcess.options.thickness.Value) + isExtraMaps = sProcess.options.thickness.Value; + else + isExtraMaps = 0; + end + sProcess.options.thickness.Value % Get subject name SubjectName = file_standardize(sProcess.options.subjectname.Value); if isempty(SubjectName) @@ -80,7 +122,7 @@ return end % Call processing function - [isOk, errMsg] = Compute(iSubject, [], nVertices, 0); + [isOk, errMsg] = Compute(iSubject, [], nVertices, TpmNii, isSphReg, isExtraMaps, 0); % Handling errors if ~isOk bst_report('Error', sProcess, [], errMsg); @@ -93,7 +135,7 @@ %% ===== COMPUTE CANONICAL SURFACES ===== -function [isOk, errMsg] = Compute(iSubject, iAnatomy, nVertices, isInteractive) +function [isOk, errMsg] = Compute(iSubject, iAnatomy, nVertices, TpmNii, isSphReg, isExtraMaps, isInteractive) isOk = 0; errMsg = ''; % Initialize SPM @@ -109,10 +151,24 @@ return; end % Check DARTEL template - dartelTpm = bst_fullfile(bst_get('SpmDir'), 'toolbox', 'cat12', 'templates_1.50mm', 'Template_1_IXI555_MNI152.nii'); + dartelTpm = bst_fullfile(bst_get('SpmDir'), 'toolbox', 'cat12', 'templates_volumes', 'Template_1_IXI555_MNI152.nii'); if ~file_exist(dartelTpm) - errMsg = ['Missing CAT12 template: ' 10 dartelTpm]; - return; + dartelTpm = bst_fullfile(bst_get('SpmDir'), 'toolbox', 'cat12', 'templates_1.50mm', 'Template_1_IXI555_MNI152.nii'); + if ~file_exist(dartelTpm) + errMsg = ['Missing CAT12 template: ' 10 dartelTpm]; + return; + else + catVer = 12; + end + else + catVer = 12.7; + end + % Get default TPM.nii template + if isempty(TpmNii) + TpmNii = bst_get('SpmTpmAtlas'); + end + if isempty(TpmNii) || ~file_exist(TpmNii) + error('Missing file TPM.nii'); end % ===== GET SUBJECT ===== @@ -192,46 +248,61 @@ end % ===== CALL CAT12 SEGMENTATION ===== - % Get TPM.nii template - tpmFile = bst_get('SpmTpmAtlas'); - if isempty(tpmFile) || ~file_exist(tpmFile) - error('Missing file TPM.nii'); - end % Create SPM batch matlabbatch{1}.spm.tools.cat.estwrite.data = {[NiiFile ',1']}; matlabbatch{1}.spm.tools.cat.estwrite.nproc = 0; - matlabbatch{1}.spm.tools.cat.estwrite.opts.tpm = {tpmFile}; + matlabbatch{1}.spm.tools.cat.estwrite.opts.tpm = {TpmNii}; matlabbatch{1}.spm.tools.cat.estwrite.opts.affreg = 'mni'; matlabbatch{1}.spm.tools.cat.estwrite.opts.biasstr = 0.5; - matlabbatch{1}.spm.tools.cat.estwrite.opts.accstr = 0.5; matlabbatch{1}.spm.tools.cat.estwrite.extopts.APP = 1070; matlabbatch{1}.spm.tools.cat.estwrite.extopts.LASstr = 0.5; matlabbatch{1}.spm.tools.cat.estwrite.extopts.gcutstr = 2; matlabbatch{1}.spm.tools.cat.estwrite.extopts.registration.dartel.darteltpm = {dartelTpm}; matlabbatch{1}.spm.tools.cat.estwrite.extopts.vox = 1.5; - matlabbatch{1}.spm.tools.cat.estwrite.extopts.restypes.fixed = [1 0.1]; - matlabbatch{1}.spm.tools.cat.estwrite.output.surface = 1; - matlabbatch{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.neuromorphometrics = 0; - matlabbatch{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.lpba40 = 0; - matlabbatch{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.cobra = 0; - matlabbatch{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.hammers = 0; - matlabbatch{1}.spm.tools.cat.estwrite.output.GM.native = 1; - matlabbatch{1}.spm.tools.cat.estwrite.output.GM.mod = 1; - matlabbatch{1}.spm.tools.cat.estwrite.output.GM.dartel = 0; - matlabbatch{1}.spm.tools.cat.estwrite.output.WM.native = 1; - matlabbatch{1}.spm.tools.cat.estwrite.output.WM.mod = 1; - matlabbatch{1}.spm.tools.cat.estwrite.output.WM.dartel = 0; matlabbatch{1}.spm.tools.cat.estwrite.output.labelnative = 1; - matlabbatch{1}.spm.tools.cat.estwrite.output.bias.warped = 1; matlabbatch{1}.spm.tools.cat.estwrite.output.jacobianwarped = 0; matlabbatch{1}.spm.tools.cat.estwrite.output.warps = [0 0]; + % Spherical registration (much slower) + if isSphReg + matlabbatch{1}.spm.tools.cat.estwrite.output.surface = 1; + else + matlabbatch{1}.spm.tools.cat.estwrite.output.surface = 5; + end + + % Switch depending on CAT12 versions + switch (catVer) + case 12 + matlabbatch{1}.spm.tools.cat.estwrite.opts.accstr = 0.5; + matlabbatch{1}.spm.tools.cat.estwrite.extopts.restypes.fixed = [1 0.1]; + matlabbatch{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.neuromorphometrics = 0; + matlabbatch{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.lpba40 = 0; + matlabbatch{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.cobra = 0; + matlabbatch{1}.spm.tools.cat.estwrite.output.ROImenu.atlases.hammers = 0; + matlabbatch{1}.spm.tools.cat.estwrite.output.GM.native = 1; + matlabbatch{1}.spm.tools.cat.estwrite.output.GM.mod = 1; + matlabbatch{1}.spm.tools.cat.estwrite.output.GM.dartel = 0; + matlabbatch{1}.spm.tools.cat.estwrite.output.WM.native = 1; + matlabbatch{1}.spm.tools.cat.estwrite.output.WM.mod = 1; + matlabbatch{1}.spm.tools.cat.estwrite.output.WM.dartel = 0; + matlabbatch{1}.spm.tools.cat.estwrite.output.bias.warped = 1; + case 12.7 + matlabbatch{1}.spm.tools.cat.estwrite.extopts.restypes.optimal = [1 0.1]; + matlabbatch{1}.spm.tools.cat.estwrite.output.ROImenu.noROI = struct([]); + matlabbatch{1}.spm.tools.cat.estwrite.output.GM.native = 1; + matlabbatch{1}.spm.tools.cat.estwrite.output.GM.mod = 0; + matlabbatch{1}.spm.tools.cat.estwrite.output.GM.dartel = 0; + matlabbatch{1}.spm.tools.cat.estwrite.output.WM.native = 1; + matlabbatch{1}.spm.tools.cat.estwrite.output.WM.mod = 0; + matlabbatch{1}.spm.tools.cat.estwrite.output.WM.dartel = 0; + matlabbatch{1}.spm.tools.cat.estwrite.output.bias.warped = 0; + end + % Run SPM batch spm_jobman('initcfg'); spm_jobman('run',matlabbatch); % ===== IMPORT OUTPUT FOLDER ===== % Import CAT12 anatomy folder - isExtraMaps = 0; isKeepMri = 1; errorMsg = import_anatomy_cat(iSubject, catDir, nVertices, isInteractive, [], isExtraMaps, isKeepMri); if ~isempty(errorMsg) @@ -251,17 +322,20 @@ function ComputeInteractive(iSubject, iAnatomy) %#ok if (nargin < 2) || isempty(iAnatomy) iAnatomy = []; end - % Open progress bar - bst_progress('start', 'CAT12', 'CAT12 MRI segmentation...'); - bst_progress('setimage', 'logo_cat.gif'); % Ask for number of vertices nVertices = java_dialog('input', 'Number of vertices on the cortex surface:', 'CAT12 segmentation', [], '15000'); if isempty(nVertices) return end nVertices = str2double(nVertices); - % Compute surfaces - [isOk, errMsg] = Compute(iSubject, iAnatomy, nVertices, 1); + % Open progress bar + bst_progress('start', 'CAT12', 'CAT12 MRI segmentation...'); + bst_progress('setimage', 'logo_cat.gif'); + % Run CAT12 + TpmNii = bst_get('SpmTpmAtlas'); + isSphReg = 1; + isExtraMaps = 0; + [isOk, errMsg] = Compute(iSubject, iAnatomy, nVertices, TpmNii, isSphReg, isExtraMaps, 1); % Error handling if ~isOk bst_error(errMsg, 'CAT12 MRI segmentation', 0); diff --git a/toolbox/process/panel_process_select.m b/toolbox/process/panel_process_select.m index 2bb355921..35957c5d1 100644 --- a/toolbox/process/panel_process_select.m +++ b/toolbox/process/panel_process_select.m @@ -2388,7 +2388,7 @@ function LoadPipelineFromFile() % Create final string optStr = [', ...' strComment, 10 strIdent '''' optNames{iOpt} ''', ' strPad str_format(optValue, 1, 2)]; % Replace raw filenames and subject names - if isfield(opt, 'Type') && ismember(opt.Type, {'filename','datafile'}) && iscell(opt.Value) + if isfield(opt, 'Type') && ismember(opt.Type, {'filename','datafile'}) && iscell(opt.Value) && ~isempty(RawFiles) % List of files if iscell(optValue{1}) for ic = 1:length(optValue{1}) @@ -3017,13 +3017,13 @@ function ParseProcessFolder(isForced) %#ok if iscell(opt.Value{1}) for ic = 1:length(opt.Value{1}) iFile = find(strcmpi(RawFiles, opt.Value{1}{ic})); - if isempty(iFile) + if isempty(iFile) && ~isempty(opt.Value{1}{ic}) RawFiles{end+1} = opt.Value{1}{ic}; end end else iFile = find(strcmpi(RawFiles, opt.Value{1})); - if isempty(iFile) + if isempty(iFile) && ~isempty(opt.Value{1}) RawFiles{end+1} = opt.Value{1}; end end