diff --git a/.codespellrc b/.codespellrc index 573b242..0a3a38e 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,4 +1,4 @@ [codespell] -skip = .git,env,*build,lib +skip = .git,env,*build,lib,*LUT.csv,*.xml builtin = clear,rare ignore-words-list = morg, fo diff --git a/.gitignore b/.gitignore index 19db602..c38881e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,24 +14,18 @@ docs/build/* # ignore virtual env envs/* -# visual studio code stuff -.vscode - atlas/visual_topography_probability_atlas/ demos/*/derivatives - #for testing coveragage_html lib/bids-matlab - ## MATLAB / OCTAVE gitignore template # From : https://github.com/github/gitignore/blob/master/Global/MATLAB.gitignore - # Windows default autosave extension *.asv diff --git a/.readthedocs.yml b/.readthedocs.yml index 71f74e4..84acb53 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -22,6 +22,6 @@ formats: # Optionally set the version of Python and requirements required to build your docs python: - version: 3.7 + version: '3.8' install: - requirements: requirements.txt diff --git a/README.md b/README.md index 02d83a1..c06bdb5 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Fire up Octave or Matlab and type ```matlab cd CPP_ROI -% Th following adds the relevant folders to your path. +% The following adds the relevant folders to your path. % This needs to be done once per session (your path will not be saved) initCppRoi @@ -64,6 +64,7 @@ as a submodule, and initialized when running `initCppSpm`. - the SPM Anatomy toolbox (INSERT URL) - the SPM neuromorphometric atlas - neurosynth probability maps + - A multi-modal parcellation of human cerebral cortex [@glasser_multi-modal_2016] - the probabilistic maps of visual topography in human cortex [@wang2014] - https://scholar.princeton.edu/napl/resources - the probabilistic functional atlas of human occipito-temporal visual diff --git a/atlas/Glasser/LUT.csv b/atlas/Glasser/LUT.csv new file mode 100644 index 0000000..0a63958 --- /dev/null +++ b/atlas/Glasser/LUT.csv @@ -0,0 +1,181 @@ +label,description,ROI +1,Primary_visual_cortex,V1 +2,Medial_superior_temporal_area,MST +3,Sixth_Visual_Area,V6 +4,Second_Visual_Area,V2 +5,Third_Visual_Area,V3 +6,Forth_Visual_Area,V4 +7,Eighth_Visual_Area,V8 +8,Primary_Motor_Cortex,4 +9,Primary_Sensory_Cortex,3b +10,Frontal_Eye_Field,FEF +11,Premotor_Eye_Field,PEF +12,Area_55b,55b +13,Area_V3A,V3A +14,RetroSplenial_Complex,RSC +15,Parieto-Occipital_Sulcus_Area_2,POS2 +16,Seventh_Visual_Area,V7 +17,IntraParietal_Sulcus_Area_1,IPS1 +18,Fusiform_Face_Complex,FFC +19,Area_V3B,V3B +20,Lateral_Occipital_Area_1,LO1 +21,Lateral_Occipital_Area_2,LO2 +22,Posterior_InferoTemporal_Complex,PIT +23,Middle_Temporal_Area,MT +24,Primary_Auditory_Cortex,A1 +25,PeriSylvian_Language_Area,PSL +26,Superior_Frontal_Language_Area,SFL +27,PreCuneus_Visual_Area,PCV +28,Superior_Temporal_Visual_Area,STV +29,Medial_Area_7P,7Pm +30,Area_7m,7m +31,Parieto-Occipital_Sulcus_Area_1,POS1 +32,Area_23d,23d +33,Area_Ventral_23_a,+bv23ab +34,Area_Dorsal_23_a,+bd23ab +35,Area_31p_Ventral,31pv +36,Area_5m,5m +37,Area_5m_ventral,5mv +38,Area_23c,23c +39,Area_5L,5L +40,Dorsal_Area_24d,24dd +41,Lateral_Area_7A,24dv +42,Supplementary_and_Cingulate_Eye_Field,7AL +43,Area_6m_Anterior,SCEF +44,Medial_6m_anterior,6ma +45,Medial_Area_7A,7Am +46,Lateral_Area_7P,7PL +47,Area_7PC,7PC +48,Area_Lateral_IntraParietal_ventral,LIPv +49,Ventral_IntraParietal_Complex,VIP +50,Medial_IntraParietal_Area,MIP +51,Area_1,1 +52,Area_2,2 +53,Area_3a,3a +54,Dorsal_area_6,6d +55,Area_6mp,6mp +56,Ventral_Area_6,6v +57,Area_Posterior_24,p24pr +58,Area_33_prime,33pr +59,Anterior_24_prime,a24pr +60,Area_p32_prime,p32pr +61,Area_a24,a24 +62,Area_dorsal_32,d32 +63,Area_8BM,8BM +64,Area_p32,p32 +65,Area_10r,10r +66,Area_47m,47m +67,Area_8Av,8Av +68,Area_8Ad,8Ad +69,Area_9_Middle,9m +70,Area_8B_Lateral,8BL +71,Area_9_Posterior,9p +72,Area_10d,10d +73,Area_8C,8C +74,Area_44,44 +75,Area_45,45 +76,Area_47L,47l +77,Area_anterior_47R,a47r +78,Rostral_Area_6,6r +79,Area_IFJa,IFJa +80,Area_IFJp,IFJp +81,Area_IFSp,IFSp +82,Area_IFSa,IFSa +83,Area_posterior_9-46v,p9-46v +84,Area_46,46 +85,Area_anterior_9-46v,a9-46v +86,Area_9-46d,9-46d +87,Area_9_anterior,9a +88,Area_10v,10v +89,Area_anterior_10p,a10p +90,Polar_10p,10pp +91,Area_11,11l +92,Area_13,13l +93,Orbital_Frontal_Complex,OFC +94,Area_47s,47s +95,Area_Lateral_IntraParietal_dorsal,LIPd +96,Area_6_anterior,6a +97,Inferior_6-8_Transitional_Area,i6-8 +98,Superior_6-8_Transitional_Area,s6-8 +99,Area_43,43 +100,Area_OP4/PV,OP4 +101,Area_OP1/SII,OP1 +102,Area_OP2-3/VS,OP2-3 +103,Area_52,52 +104,RetroInsular_Cortex,RI +105,Area_PFcm,PFcm +106,Posterior_Insular_Area_2,PoI2 +107,Area_TA2,TA2 +108,Frontal_OPercular_Area_4,FOP4 +109,Middle_Insular_Area,MI +110,Pirform_Cortex,Pir +111,Anterior_Ventral_Insular_Area,AVI +112,Anterior_Agranular_Insula_Complex,AAIC +113,Frontal_OPercular_Area_1,FOP1 +114,Frontal_OPercular_Area_3,FOP3 +115,Frontal_OPercular_Area_2,FOP2 +116,Area_PFt,PFt +117,Anterior_IntraParietal_Area,AIP +118,Entorhinal_Cortex,EC +119,PreSubiculum,PreS +120,Hippocampus,H +121,ProStraite_Area,ProS +122,Perirhinal_Ectorhinal_Cortex,PeEc +123,Area_STGa,STGa +124,ParaBelt_Complex,PBelt +125,Auditory_5_Complex,A5 +126,ParaHippocampal_Area_1,PHA1 +127,ParaHippocampal_Area_3,PHA3 +128,Area_STSd_Anterior,STSda +129,Area_STSd_Posterior,STSdp +130,Area_STSv_Posterior,STSvp +131,Area_TG_Dorsal,TGd +132,Area_TE1_Anterior,TE1a +133,Area_TE1_Posterior,TE1p +134,Area_TE2_Anterior,TE2a +135,Area_TF,TF +136,Area_TE2_Posterior,TE2p +137,Area_PHT,PHT +138,Area_PH,PH +139,Area_TemporoParietoOccipital_Junction_1,TPOJ1 +140,Area_TemporoParietoOccipital_Junction_2,TPOJ2 +141,Area_TemporoParietoOccipital_Junction_3,TPOJ3 +142,Dorsal_Transitional_Visual_Area,DVT +143,Area_PGp,PGp +144,Area_IntraParietal_2,IP2 +145,Area_IntraParietal_1,IP1 +146,Area_IntraParietal_0,IP0 +147,Area_PF_Opercular,PFop +148,Area_PF_Complex,PF +149,Area_PFm_Complex,PFm +150,Area_PGi,PGi +151,Area_PGs,PGs +152,Area_V6A,V6A +153,VentroMedial_Visual_Area_1,VMV1 +154,VentroMedial_Visual_Area_3,VMV3 +155,ParaHippocampal_Area_2,PHA2 +156,Area_V4t,V4t +157,Area_FST,FST +158,Area_V3CD,V3CD +159,Area_Lateral_Occipital_3,LO3 +160,VentroMedial_Visual_Area_2,VMV2 +161,Area_31pd,31pd +162,Area_31a,31a +163,Ventral_Visual_Complex,VVC +164,Area_25,25 +165,Area_s32,s32 +166,Posterior_OFC_Complex,pOFC +167,Area_Posterior_Insular_1,PoI1 +168,Insular_Granular_Complex,Ig +169,Area_Frontal_Opercular_5,FOP5 +170,Area_Posterior_10p,p10p +171,Area_Posterior_47r,p47r +172,Area_TG_ventral,TGv +173,Medial_Belt_Complex,MBelt +174,Lateral_Belt_Complex,LBelt +175,Auditory_4_Complex,A4 +176,Area_STSv_Anterior,STSva +177,Area_TE1_Middle,TE1m +178,Para-Insular_Area,PI +179,Area_Anterior_32_Prime,a32pr +180,Area_Posterior_24,p24 diff --git a/atlas/Glasser/README.md b/atlas/Glasser/README.md new file mode 100644 index 0000000..8a354fb --- /dev/null +++ b/atlas/Glasser/README.md @@ -0,0 +1,37 @@ +Taken from: + +https://figshare.com/articles/dataset/HCP-MMP1_0_projected_on_MNI2009a_GM_volumetric_in_NIfTI_format/3501911 + +A volumetric version in MNI/ICBM 152 2009b NLIN Asymmetric space + +Label description were taken from: + +https://github.com/neurodata/neuroparc/blob/master/atlases/label/Human/Anatomical-labels-csv/Glasser.csv + +Related citations: + +Glasser, M. F., Coalson, T. S., Robinson, E. C., Hacker, C. D., Harwell, J., +Yacoub, E., et al. (2016). A multi-modal parcellation of human cerebral cortex. +Nature. http://doi.org/10.1038/nature18933 + +Based on the Freesurfer version of the HCP-MMP1.0 parcellation +(https://figshare.com/articles/HCP-MMP1_0_projected_on_fsaverage/3498446), I +created a volumetric version of the HCP-MMP1.0 parcellation that was published +by Glasser et al. (Nature, 2016). + +Please note that this volumetric version should be used and applied with great +care – and under certain circumstances only. According to the authors of the +original work, this fine parcellation could not have been achieved on a +volumetric basis but was only realizable using a multimodal surface-based +analysis (and co-registration). Thus, this volumetric version of their work is +not ideally suited to be used for standard brain-mapping approaches that use +(volumetric) nonlinear deformations. It is rather thought to be an accompanying +volumetric version to the surface-based version. Also, it could be used for +comparative purposes within MNI space (e.g. to compare it to other cortical +parcellations available in this space such as the AAL, Hammersmith, AICHA, +Mindboggle 101 atlases). + +The conversion to volumetric space (projection to the ICBM 152 2009a NLIN +version within MNI space) is based on the fsaverage version of the HCP-MMP1.0 +available here +(https://figshare.com/articles/HCP-MMP1_0_projected_on_fsaverage/3498446). diff --git a/atlas/Glasser/space-MNI152ICBM2009anlin_atlas-glasser_dseg.nii.gz b/atlas/Glasser/space-MNI152ICBM2009anlin_atlas-glasser_dseg.nii.gz new file mode 100644 index 0000000..4b74e25 Binary files /dev/null and b/atlas/Glasser/space-MNI152ICBM2009anlin_atlas-glasser_dseg.nii.gz differ diff --git a/atlas/Glasser/space-MNI152ICBM2009anlin_atlas-glasser_dseg.xml b/atlas/Glasser/space-MNI152ICBM2009anlin_atlas-glasser_dseg.xml new file mode 100644 index 0000000..42ba1a8 --- /dev/null +++ b/atlas/Glasser/space-MNI152ICBM2009anlin_atlas-glasser_dseg.xml @@ -0,0 +1,193 @@ + + +
+ space-MNI152ICBM2009anlin_atlas-glasser_dseg + 1.0 + space-MNI152ICBM2009anlin_atlas-glasser_dseg + + + MNI + Label + + space-MNI152ICBM2009anlin_atlas-glasser_dseg.nii + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/atlas/returnAtlasDir.m b/atlas/returnAtlasDir.m index b2e3732..9106a15 100644 --- a/atlas/returnAtlasDir.m +++ b/atlas/returnAtlasDir.m @@ -28,6 +28,9 @@ case 'hcpex' atlasDir = fullfile(atlasDir, 'HCPex', 'HCPex_v1.0'); + case 'glasser' + atlasDir = fullfile(atlasDir, 'Glasser'); + case 'neuromorphometrics' otherwise diff --git a/atlas/update_atlas_xml.py b/atlas/update_atlas_xml.py new file mode 100644 index 0000000..7fa04dd --- /dev/null +++ b/atlas/update_atlas_xml.py @@ -0,0 +1,40 @@ +"""Reads the content of the LUT.csv and updates the xml file with the ROI names.""" + +from rich import print +import pandas as pd +import xml.etree.ElementTree as ET +from pathlib import Path + +lut_file = ( + Path(__file__).parent + / "Glasser" + / "LUT.csv" +) + +df = pd.read_csv(lut_file, sep=",") + +xml_file = ( + Path(__file__).parent + / "Glasser" + / "space-MNI152ICBM2009anlin_atlas-glasser_dseg.xml" +) + +mytree = ET.parse(xml_file) +print(mytree) +myroot = mytree.getroot() + +for label in myroot.iter("label"): + + index = label.find('index').text + name = label.find('name').text + print(f"XML: {index} - {name}") + + df_row = df.loc[df['label'] == int(index)] + ROI = df_row['ROI'].values[0] + print(f"LUT: {index} - {ROI}") + + if name != ROI: + print(f"Updating {name} to {ROI}") + label.find('name').text = ROI + +mytree.write(xml_file) diff --git a/atlas/visfAtlas/README.md b/atlas/visfAtlas/README.md index d4ceace..f09c94a 100755 --- a/atlas/visfAtlas/README.md +++ b/atlas/visfAtlas/README.md @@ -1,16 +1,24 @@ visfAtlas in nifti volume format -The visfAtlas is based on N19 subjects. In FreeSurfer, it is aligned to the fsaverage brain. In BrainVoyager, it is aligned to a newly generated average brain for BrainVoyager, called the BVaverage. The BVaverage is distributed with this publication. The nifti volume atlas is aligned to the MNI colin27 brain. +The visfAtlas is based on N19 subjects. In FreeSurfer, it is aligned to the +fsaverage brain. In BrainVoyager, it is aligned to a newly generated average +brain for BrainVoyager, called the BVaverage. The BVaverage is distributed with +this publication. The nifti volume atlas is aligned to the MNI colin27 brain. Citing: -Please cite the article for use of the functional atlas as well as the BVaverage. +Please cite the article for use of the functional atlas as well as the +BVaverage. -Rosenke, M., van Hoof, R., van den Hurk, J., Goebel, R. (2020). A probabilistic functional parcellation of human occipito-temporal cortex. TBD +Rosenke, M., van Hoof, R., van den Hurk, J., Goebel, R. (2020). A probabilistic +functional parcellation of human occipito-temporal cortex. TBD -To acknowledge using this atlas in your research, you might include a sentence like one of the following: +To acknowledge using this atlas in your research, you might include a sentence +like one of the following: -"We used the functional atlas of visual cortex developed by Rosenke et al. (2020)…” +"We used the functional atlas of visual cortex developed by Rosenke et al. +(2020)…” - -The maximum probability map can be visualized by opening the visfATlas\_volume.nii.gz file and loading the correct color map (visfAtlas\_FSL.cmap) through the overlay display panel. +The maximum probability map can be visualized by opening the +visfATlas_volume.nii.gz file and loading the correct color map +(visfAtlas_FSL.cmap) through the overlay display panel. diff --git a/initCppRoi.m b/initCppRoi.m index 81c52d8..3bee8ae 100644 --- a/initCppRoi.m +++ b/initCppRoi.m @@ -30,6 +30,7 @@ function initCppRoi() copyAtlasToSpmDir('HCPex', 'verbose', true); copyAtlasToSpmDir('AAL', 'verbose', true); + copyAtlasToSpmDir('Glasser', 'verbose', true); CPP_ROI_INITIALIZED = true(); diff --git a/references.bib b/references.bib index b11f8d9..bc6c3e5 100644 --- a/references.bib +++ b/references.bib @@ -43,3 +43,21 @@ @article{huang_extended_2022 year = {2022}, pages = {763--778}, } + + +@article{glasser_multi-modal_2016, + title = {A multi-modal parcellation of human cerebral cortex}, + volume = {536}, + issn = {0028-0836, 1476-4687}, + url = {http://www.nature.com/articles/nature18933}, + doi = {10.1038/nature18933}, + language = {en}, + number = {7615}, + urldate = {2023-05-18}, + journal = {Nature}, + author = {Glasser, Matthew F. and Coalson, Timothy S. and Robinson, Emma C. and Hacker, Carl D. and Harwell, John and Yacoub, Essa and Ugurbil, Kamil and Andersson, Jesper and Beckmann, Christian F. and Jenkinson, Mark and Smith, Stephen M. and Van Essen, David C.}, + month = aug, + year = {2016}, + pages = {171--178}, + file = {Accepted Version:/home/remi/Zotero/storage/6R5464JP/Glasser et al. - 2016 - A multi-modal parcellation of human cerebral corte.pdf:application/pdf}, +} diff --git a/src/atlas/copyAtlasToSpmDir.m b/src/atlas/copyAtlasToSpmDir.m index f2ae64b..62b7ba6 100644 --- a/src/atlas/copyAtlasToSpmDir.m +++ b/src/atlas/copyAtlasToSpmDir.m @@ -16,6 +16,8 @@ function copyAtlasToSpmDir(varargin) % (C) Copyright 2022 CPP ROI developers + SUPPORTED_ATLASES = {'aal', 'hcpex'}; + args = inputParser; addOptional(args, 'atlas', 'AAL', @ischar); @@ -35,10 +37,21 @@ function copyAtlasToSpmDir(varargin) sourceAtlasXml = fullfile(returnAtlasDir(), 'AAL3', 'AAL3v1_1mm.xml'); case 'hcpex' - unzipAtlas('hcpex'); - sourceAtlasImage = fullfile(returnAtlasDir('hcpex'), 'HCPex.nii'); + unzipAtlas(lower(atlas)); + sourceAtlasImage = fullfile(returnAtlasDir(lower(atlas)), 'HCPex.nii'); sourceAtlasXml = fullfile(returnAtlasDir(), 'HCPex.xml'); + case 'glasser' + unzipAtlas(lower(atlas)); + sourceAtlasImage = fullfile(returnAtlasDir(lower(atlas)), ... + 'space-MNI152ICBM2009anlin_atlas-glasser_dseg.nii'); + sourceAtlasXml = fullfile(returnAtlasDir(lower(atlas)), ... + 'space-MNI152ICBM2009anlin_atlas-glasser_dseg.xml'); + + otherwise + error(['Only the following atlases can be copied to SPM atlas folder:\n', ... + bids.internal.create_unordered_list(SUPPORTED_ATLASES)]); + end targetAtlasImage = fullfile(spmAtlasDir, spm_file(sourceAtlasImage, 'filename')); @@ -58,8 +71,11 @@ function copyAtlasToSpmDir(varargin) copyfile(sourceAtlasImage, spmAtlasDir); - if exist(fullfile(spmAtlasDir, '*.nii.gz')) - gunzip(fullfile(spmAtlasDir, '*.nii.gz')); + unZippedAtlases = spm_select('FPList', spmAtlasDir, '.*.nii.gz'); + if ~isempty(unZippedAtlases) + for i = 1:size(unZippedAtlases, 1) + gunzip(deblank(unZippedAtlases(i, :))); + end delete(fullfile(spmAtlasDir, '*.nii.gz')); end diff --git a/src/atlas/extractRoiByLabel.m b/src/atlas/extractRoiByLabel.m index acbfd39..5e3dfea 100644 --- a/src/atlas/extractRoiByLabel.m +++ b/src/atlas/extractRoiByLabel.m @@ -24,6 +24,12 @@ outputVol = false(size(vol)); outputVol(vol == labelStruct.label) = true; + if sum(outputVol(:)) == 0 + warning('No voxel in ROI with value "%i"\n in image %s', ... + labelStruct.label, ... + sourceImage); + end + bf = bids.File(sourceImage); bf.entities.label = bids.internal.camel_case(labelStruct.ROI); bf.suffix = 'mask'; diff --git a/src/atlas/extractRoiFromAtlas.m b/src/atlas/extractRoiFromAtlas.m index 6b806e9..ed339ae 100644 --- a/src/atlas/extractRoiFromAtlas.m +++ b/src/atlas/extractRoiFromAtlas.m @@ -10,7 +10,9 @@ % :param outputDir: % :type outputDir: string % - % :param atlasName: ``'wang'``, ``'visfatlas'``, ``'neuromorphometrics'``, ``'hcpex'`` + % :param atlasName: ``'wang'``, ``'visfatlas'``, + % ``'neuromorphometrics'``, ``'hcpex'``, + % ``'glasser'`` % :type atlasName: string % % :param roiName: run ``getLookUpTable(atlasName)`` to get a list of ROI names to choose from @@ -50,43 +52,48 @@ [atlasFile, lut] = getAtlasAndLut(atlasName); - if strcmpi(atlasName, 'wang') + switch lower(atlasName) + case 'wang' - if strcmpi(hemisphere, 'L') - atlasFile = atlasFile(1, :); - else - atlasFile = atlasFile(2, :); - end + if strcmpi(hemisphere, 'L') + atlasFile = atlasFile(1, :); + else + atlasFile = atlasFile(2, :); + end - roiIdx = strcmp(roiName, lut.ROI); + roiIdx = strcmp(roiName, lut.ROI); - elseif strcmpi(atlasName, 'neuromorphometrics') + case 'neuromorphometrics' - roiName = regexprep(roiName, '(Left )|(Right )', ''); + roiName = regexprep(roiName, '(Left )|(Right )', ''); - prefix = ''; - if strcmp(hemisphere, 'L') - prefix = 'Left '; - elseif strcmp(hemisphere, 'R') - prefix = 'Right '; - end + prefix = ''; + if strcmp(hemisphere, 'L') + prefix = 'Left '; + elseif strcmp(hemisphere, 'R') + prefix = 'Right '; + end - roiIdx = strcmp([prefix roiName], lut.ROI); + roiIdx = strcmp([prefix roiName], lut.ROI); - elseif strcmpi(atlasName, 'visfatlas') + case 'visfatlas' - prefix = ''; - if strcmp(hemisphere, 'L') - prefix = 'lh_'; - elseif strcmp(hemisphere, 'R') - prefix = 'rh_'; - end + prefix = ''; + if strcmp(hemisphere, 'L') + prefix = 'lh_'; + elseif strcmp(hemisphere, 'R') + prefix = 'rh_'; + end - roiIdx = strcmp([prefix roiName], lut.ROI); + roiIdx = strcmp([prefix roiName], lut.ROI); - elseif strcmpi(atlasName, 'hcpex') + case 'hcpex' - roiIdx = strcmp([hemisphere '_' roiName], lut.ROI); + roiIdx = strcmp([hemisphere '_' roiName], lut.ROI); + + case 'glasser' + + roiIdx = strcmp(roiName, lut.ROI); end @@ -103,10 +110,17 @@ roiImage = extractRoiByLabel(atlasFile, labelStruct); + if strcmpi(atlasName, 'glasser') + tmp = roiImage; + roiImage = keepHemisphere(tmp, hemisphere, false); + delete(tmp); + delete(spm_file(tmp, 'ext', '.json')); + end + % rename file entities = struct('hemi', hemisphere, ... 'space', 'MNI', ... - 'atlas', atlasName, ... + 'atlas', lower(atlasName), ... 'label', roiName); nameStructure = struct('entities', entities, ... 'suffix', 'mask', ... diff --git a/src/atlas/getAtlasFilename.m b/src/atlas/getAtlasFilename.m index d0f5073..5a967c4 100644 --- a/src/atlas/getAtlasFilename.m +++ b/src/atlas/getAtlasFilename.m @@ -45,6 +45,10 @@ atlasFilename = fullfile(atlasDir, 'HCPex.nii'); + case 'glasser' + + atlasFilename = fullfile(atlasDir, 'space-MNI152ICBM2009anlin_atlas-glasser_dseg.nii'); + end end diff --git a/src/atlas/getLookUpTable.m b/src/atlas/getLookUpTable.m index 97b4f81..5b4138a 100644 --- a/src/atlas/getLookUpTable.m +++ b/src/atlas/getLookUpTable.m @@ -24,9 +24,9 @@ switch lower(atlasName) - case 'wang' + case {'wang', 'glasser'} - unzipAtlas('wang'); + unzipAtlas(lower(atlasName)); roiLabelLUT = spm_load(fullfile(atlasDir, 'LUT.csv')); diff --git a/src/atlas/isAKnownAtlas.m b/src/atlas/isAKnownAtlas.m index 3c49d4e..41c3e97 100644 --- a/src/atlas/isAKnownAtlas.m +++ b/src/atlas/isAKnownAtlas.m @@ -1,10 +1,11 @@ function isAKnownAtlas(atlasName) % (C) Copyright 2021 CPP ROI developers - if ~ismember(lower(atlasName), {'wang', ... + if ~ismember(lower(atlasName), {'anatomy_toobox', ... + 'glasser', ... + 'hcpex', ... 'neuromorphometrics', ... - 'anatomy_toobox', ... 'visfatlas', ... - 'hcpex'}) + 'wang'}) % TODO throw a proper error here error('unknown atlas type'); end diff --git a/src/atlas/unzipAtlas.m b/src/atlas/unzipAtlas.m index 14ecd88..7549f0f 100644 --- a/src/atlas/unzipAtlas.m +++ b/src/atlas/unzipAtlas.m @@ -29,31 +29,33 @@ function unzipAtlas(atlas) end - case 'visfatlas' - - file = fullfile(atlasDir, 'visfAtlas/space-MNI_atlas-visfAtlas_dseg.nii.gz'); + case 'glasser' + file = fullfile(atlasDir, 'Glasser', 'space-MNI152ICBM2009anlin_atlas-glasser_dseg.nii'); + gunzipAtlasIfNecessary(file); - if bids.internal.is_octave() - gunzipWithOctave(file); - else - gunzip(file); - end + case 'visfatlas' + file = fullfile(atlasDir, 'visfAtlas', 'space-MNI_atlas-visfAtlas_dseg.nii'); + gunzipAtlasIfNecessary(file); case 'hcpex' + file = fullfile(returnAtlasDir('hcpex'), 'HCPex.nii'); + gunzipAtlasIfNecessary(file); - if exist(fullfile(returnAtlasDir('hcpex'), 'HCPex.nii'), 'file') - return - end + end - file = fullfile(returnAtlasDir('hcpex'), 'HCPex.nii.gz'); - if bids.internal.is_octave() - gunzipWithOctave(file); - else - gunzip(file); - end +end +function gunzipAtlasIfNecessary(file) + if exist(file, 'file') + return end + file = [file '.gz']; + if bids.internal.is_octave() + gunzipWithOctave(file); + else + gunzip(file); + end end function gunzipWithOctave(file) diff --git a/src/roi/keepHemisphere.m b/src/roi/keepHemisphere.m index 4619a56..2fd1714 100644 --- a/src/roi/keepHemisphere.m +++ b/src/roi/keepHemisphere.m @@ -1,4 +1,4 @@ -function outputImage = keepHemisphere(inputImage, hemisphere) +function outputImage = keepHemisphere(inputImage, hemisphere, paddingValue) % % Only keep the values from one hemisphere. Sets the other half to NaN. % Writes an image with an extra entity ``_hemi-[R|L]`` @@ -18,6 +18,10 @@ % TODO change the hemi entity + if nargin < 3 + paddingValue = nan(); + end + hdr = spm_vol(inputImage); vol = spm_read_vols(hdr); @@ -37,7 +41,7 @@ end - vol(discard, :, :) = NaN; + vol(discard, :, :) = paddingValue; bf = bids.File(inputImage); bf.entity_order = cat(1, 'hemi', fieldnames(bf.entities)); diff --git a/tests/test_copyAtlasToSpmDir.m b/tests/test_copyAtlasToSpmDir.m index 935b9fb..ff2e734 100644 --- a/tests/test_copyAtlasToSpmDir.m +++ b/tests/test_copyAtlasToSpmDir.m @@ -7,6 +7,24 @@ initTestSuite; end +function test_copyAtlasToSpmDir_glasser() + + if bids.internal.is_github_ci() + return + end + + copyAtlasToSpmDir('Glasser', 'verbose', false); + + targetAtlasImage = fullfile(spmAtlasDir(), ... + 'space-MNI152ICBM2009anlin_atlas-glasser_dseg.nii'); + targetAtlasXml = fullfile(spmAtlasDir(), ... + 'space-MNI152ICBM2009anlin_atlas-glasser_dseg.xml'); + + assertEqual(exist(targetAtlasImage, 'file'), 2); + assertEqual(exist(targetAtlasXml, 'file'), 2); + +end + function test_copyAtlasToSpmDir_basic() if bids.internal.is_github_ci() @@ -15,10 +33,8 @@ function test_copyAtlasToSpmDir_basic() copyAtlasToSpmDir('AAL', 'verbose', false); - spmAtlasDir = fullfile(spm('dir'), 'atlas'); - - targetAtlasImage = fullfile(spmAtlasDir, 'AAL3v1_1mm.nii'); - targetAtlasXml = fullfile(spmAtlasDir, 'AAL3v1_1mm.xml'); + targetAtlasImage = fullfile(spmAtlasDir(), 'AAL3v1_1mm.nii'); + targetAtlasXml = fullfile(spmAtlasDir(), 'AAL3v1_1mm.xml'); assertEqual(exist(targetAtlasImage, 'file'), 2); assertEqual(exist(targetAtlasXml, 'file'), 2); @@ -33,12 +49,14 @@ function test_copyAtlasToSpmDir_HPCex() copyAtlasToSpmDir('HCPex', 'verbose', false); - spmAtlasDir = fullfile(spm('dir'), 'atlas'); - - targetAtlasImage = fullfile(spmAtlasDir, 'HCPex.nii'); - targetAtlasXml = fullfile(spmAtlasDir, 'HCPex.xml'); + targetAtlasImage = fullfile(spmAtlasDir(), 'HCPex.nii'); + targetAtlasXml = fullfile(spmAtlasDir(), 'HCPex.xml'); assertEqual(exist(targetAtlasImage, 'file'), 2); assertEqual(exist(targetAtlasXml, 'file'), 2); end + +function value = spmAtlasDir() + value = fullfile(spm('dir'), 'atlas'); +end diff --git a/tests/test_extractRoiFromAtlas.m b/tests/test_extractRoiFromAtlas.m index 3acff73..ad38efc 100644 --- a/tests/test_extractRoiFromAtlas.m +++ b/tests/test_extractRoiFromAtlas.m @@ -8,6 +8,22 @@ initTestSuite; end +function test_extractRoiFromAtlas_glasser() + + roiImage = extractRoiFromAtlas(pwd, 'Glasser', 'FEF', 'L'); + + assertEqual(exist(fullfile(pwd, 'hemi-L_space-MNI_atlas-glasser_label-FEF_mask.nii'), ... + 'file'), ... + 2); + + vol = spm_read_vols(spm_vol(roiImage)); + assertEqual(sum(vol(:) > 0), 13460); % check the ROI has the right number of voxel + + delete(fullfile(pwd, '*.nii')); + delete(fullfile(pwd, '*.json')); + +end + function test_extractRoiFromAtlas_wang() roiImage = extractRoiFromAtlas(pwd, 'wang', 'V1v', 'L'); diff --git a/tests/test_getAtlasFilename.m b/tests/test_getAtlasFilename.m index 4ca4ede..ea3dba8 100644 --- a/tests/test_getAtlasFilename.m +++ b/tests/test_getAtlasFilename.m @@ -8,6 +8,13 @@ initTestSuite; end +function test_getAtlasFilename_glasser() + + atlasFilename = getAtlasFilename('Glasser'); + assertEqual(size(atlasFilename, 1), 1); + +end + function test_getAtlasFilename_wang() rmRetinoAtlas(); diff --git a/tests/test_getLookUpTable.m b/tests/test_getLookUpTable.m index f5e62ab..016d886 100644 --- a/tests/test_getLookUpTable.m +++ b/tests/test_getLookUpTable.m @@ -1,6 +1,6 @@ -% (C) Copyright 2020 CPP ROI developers - function test_suite = test_getLookUpTable() %#ok<*STOUT> + % (C) Copyright 2020 CPP ROI developers + try % assignment of 'localfunctions' is necessary in Matlab >= 2016 test_functions = localfunctions(); %#ok<*NASGU> catch % no problem; early Matlab versions can use initTestSuite fine @@ -8,6 +8,15 @@ initTestSuite; end +function test_lut_glasser_case() + + lut = getLookUpTable('Glasser'); + + assertEqual(lut.label, [1:180]'); + assertEqual(lut.ROI{1}, 'V1'); + +end + function test_lut_wang() rmRetinoAtlas(); diff --git a/tests/test_unzipAtlas.m b/tests/test_unzipAtlas.m index d5d2b48..041cf25 100644 --- a/tests/test_unzipAtlas.m +++ b/tests/test_unzipAtlas.m @@ -8,7 +8,20 @@ initTestSuite; end -function test_unzipAtlas_default() +function test_unzipAtlas_glasser() + + unzipAtlas('Glasser'); + + expectedFile = fullfile(returnAtlasDir('Glasser'), ... + 'space-MNI152ICBM2009anlin_atlas-glasser_dseg.nii'); + + assertEqual(exist(expectedFile, 'file'), 2); + + delete(expectedFile); + +end + +function test_unzipAtlas_wang() cleanUp(); @@ -20,15 +33,17 @@ function test_unzipAtlas_default() 'space-MNI_hemi-lh_dseg.nii'), ... 'file'), ... 2); +end + +function test_unzipAtlas_hcpex() unzipAtlas('hcpex'); - assertEqual(exist( ... - fullfile(returnAtlasDir('hcpex'), 'HCPex.nii'), ... - 'file'), ... - 2); + expectedFile = fullfile(returnAtlasDir('hcpex'), 'HCPex.nii'); - cleanUp(); + assertEqual(exist(expectedFile, 'file'), 2); + + delete(expectedFile); end