├── LICENSE ├── README.md └── matlab ├── example01_exploredata.m ├── example02_loaddata.m ├── example03_understandspaces.m ├── example04_surfacevisualization.m ├── example05_inspectdataatscale.m ├── example06_basicbetaloading.m ├── example07_simplecontrast.m ├── example08_simplecontrastmvpa.m ├── example09_behavioraldata.m ├── example10_encodingmodel.m ├── example11_rsa.m ├── example12_rsfc.m └── html ├── example01_exploredata.html ├── example02_loaddata.html ├── example02_loaddata.png ├── example02_loaddata_01.png ├── example02_loaddata_02.png ├── example02_loaddata_03.png ├── example02_loaddata_04.png ├── example02_loaddata_05.png ├── example02_loaddata_06.png ├── example02_loaddata_07.png ├── example02_loaddata_08.png ├── example03_understandspaces.html ├── example04_surfacevisualization.html ├── example04_surfacevisualization.png ├── example04_surfacevisualization_01.png ├── example04_surfacevisualization_02.png ├── example04_surfacevisualization_03.png ├── example04_surfacevisualization_04.png ├── example04_surfacevisualization_05.png ├── example04_surfacevisualization_06.png ├── example04_surfacevisualization_07.png ├── example04_surfacevisualization_08.png ├── example05_inspectdataatscale.html ├── example05_inspectdataatscale.png ├── example05_inspectdataatscale_01.png ├── example05_inspectdataatscale_02.png ├── example06_basicbetaloading.html ├── example06_basicbetaloading.png ├── example06_basicbetaloading_01.png ├── example06_basicbetaloading_02.png ├── example06_basicbetaloading_03.png ├── example06_basicbetaloading_04.png ├── example06_basicbetaloading_05.png ├── example06_basicbetaloading_06.png ├── example06_basicbetaloading_07.png ├── example07_simplecontrast.html ├── example07_simplecontrast.png ├── example07_simplecontrast_01.png ├── example07_simplecontrast_02.png ├── example07_simplecontrast_03.png ├── example07_simplecontrast_04.png ├── example07_simplecontrast_05.png ├── example07_simplecontrast_06.png ├── example07_simplecontrast_07.png ├── example08_simplecontrastmvpa.html ├── example08_simplecontrastmvpa.png ├── example08_simplecontrastmvpa_01.png ├── example08_simplecontrastmvpa_02.png ├── example09_behavioraldata.html ├── example09_behavioraldata.png ├── example09_behavioraldata_01.png ├── example09_behavioraldata_02.png ├── example09_behavioraldata_03.png ├── example09_behavioraldata_04.png ├── example10_encodingmodel.html ├── example10_encodingmodel.png ├── example10_encodingmodel_01.png ├── example10_encodingmodel_02.png ├── example10_encodingmodel_03.png ├── example10_encodingmodel_04.png ├── example10_encodingmodel_05.png ├── example10_encodingmodel_06.png ├── example10_encodingmodel_07.png ├── example11_rsa.html ├── example11_rsa.png ├── example11_rsa_01.png ├── example12_rsfc.html ├── example12_rsfc.png ├── example12_rsfc_01.png ├── example12_rsfc_02.png └── index.html /README.md: -------------------------------------------------------------------------------- 1 | # nsdexamples 2 | 3 | This repository contains example scripts that load and analyze the NSD data. 4 | 5 | Outputs from the scripts (via MATLAB's publish) are available here: 6 | https://htmlpreview.github.io/?https://github.com/kendrickkay/nsdexamples/blob/master/matlab/html/index.html 7 | 8 | The example scripts in this repository have the following MATLAB dependencies: 9 | - analyzePRF [http://github.com/kendrickkay/analyzePRF/] 10 | - cvncode [http://github.com/kendrickkay/cvncode/] 11 | - GLMdenoise [http://github.com/kendrickkay/GLMdenoise/] 12 | - knkutils [http://github.com/kendrickkay/knkutils/] 13 | - nsdcode [http://github.com/kendrickkay/nsdcode/] 14 | - freesurfer/matlab [matlab functions provided with FreeSurfer] 15 | - NIfTI_20140122 [https://www.mathworks.com/matlabcentral/fileexchange/8797-tools-for-nifti-and-analyze-image] 16 | - xticklabel_rotate [https://www.mathworks.com/matlabcentral/fileexchange/3486-xticklabel_rotate] 17 | 18 | Some of the examples use the following applications: 19 | - FreeSurfer [specifically, the freeview application] 20 | - ITK-SNAP 21 | - HandBrake 22 | -------------------------------------------------------------------------------- /matlab/example01_exploredata.m: -------------------------------------------------------------------------------- 1 | %% Example 1: Basic exploration of the NSD data files 2 | 3 | %% Introduction 4 | 5 | % In this script, we are going to do some initial exploration of the 6 | % types of data files that are made available as part of the prepared 7 | % NSD data. For this, we are going to use ITK-SNAP (as a volume viewer) 8 | % and freeview (as a surface viewer). 9 | % 10 | % Skills/concepts: 11 | % - How to use ITK-SNAP and freeview 12 | % - The nature of volumes and surfaces 13 | % - The different types of FreeSurfer surfaces 14 | % - MNI and fsaverage spaces 15 | 16 | 17 | 18 | %% Use ITK-SNAP to explore volume-format data 19 | 20 | % Load the following file into ITK-SNAP: 21 | % ~/nsd/nsddata/ppdata/subj01/anat/T1_0pt8_masked.nii.gz 22 | %% 23 | 24 | % Play around in ITK-SNAP and know how to: 25 | % - Navigate / Zoom / Pan 26 | % - Change the colormap and color range 27 | % - View the NIFTI header information (e.g. resolution, origin, orientation) 28 | %% 29 | 30 | % Load as a second image the following file: 31 | % ~/nsd/nsddata/ppdata/subj01/anat/roi/visualsulc.nii.gz 32 | %% 33 | 34 | % Play around and know how to: 35 | % - Flip through different volumes ([ or ]) 36 | % - Overlay volumes using a thresholding approach 37 | % - Overlay volumes using transparency 38 | % - Toggle overlays on/off (w) 39 | %% 40 | 41 | % Draw an ROI and save the ROI out as a NIFTI file called: 42 | % testroi.nii.gz 43 | % Then load that file back into ITK-SNAP. 44 | %% 45 | 46 | % Start over. Load T1_0pt8_masked.nii.gz as the main image, 47 | % and load roi/visualsulc.nii.gz as the segmentation image. 48 | %% 49 | 50 | % Using Label Editor, hide all of the labels and show only 51 | % Label 3 (fusiform gyrus). Play around with the 3D volume rendering. 52 | % Information on the meaning of the values in visualsulc is in: 53 | % ~/nsd/nsddata/freesurfer/fsaverage/label/visualsulc.mgz.ctab 54 | %% 55 | 56 | % Quit ITK-SNAP and try launching it from the command-line: 57 | % cd ~/nsd/nsddata/ppdata/subj01/anat/ 58 | % itksnap T1_0pt8_masked.nii.gz 59 | %% 60 | 61 | % There are many other volume viewers out there. 62 | % If you are curious, you can try, for example, mricro. 63 | 64 | 65 | 66 | %% Inspect NSD anatomical data (anat) 67 | 68 | % The prepared NSD data come in 3 anatomical spaces (0.5 mm, 0.8 mm, and 1.0 mm). 69 | % These different spaces are exactly coincident and share exactly the same 70 | % field-of-view and origin. (The origin is placed at the center of each volume.) 71 | 72 | % Load the 0.5-mm and 1.0-mm T1 volumes as a main image and an additional image, 73 | % respectively. Compare the two volumes. 74 | %% 75 | 76 | % Load the 0.5-mm and 1.0-mm T1 volumes in two separate ITK-SNAP sessions with 77 | % synchronized views (see Preferences). Play around a little. 78 | %% 79 | 80 | % Assess the co-registration quality between the 0.8-mm T1 and 0.8-mm T2. 81 | % Notice that compared to the T2, the T1 has a little bit of dropout in 82 | % the ventral part of the frontal lobe. 83 | %% 84 | 85 | % Inspect the Kastner atlas (roi/Kastner2015.nii.gz) overlaid on the 0.8-mm T1. 86 | % Information on the meaning of the values is in: 87 | % ~/nsd/nsddata/freesurfer/fsaverage/label/Kastner2015.mgz.ctab 88 | %% 89 | 90 | % Explore and visualize some additional volumes: 91 | % - aseg (anatomical segmentation from FreeSurfer) 92 | % - brainmask (liberal brain mask used to de-identify data and reduce file sizes) 93 | % - EPI_to_anat1pt0 (mean EPI volume that has been warped to the anat1pt0 space) 94 | %% 95 | 96 | % Thus far, we have been visualizing data from a single subject. 97 | % To facilitate comparison across subjects, we can put subjects in a common space. 98 | % A typical one is MNI space, and the prepared NSD data already include some 99 | % MNI transformations for your convenience. 100 | 101 | % To determine the transformation between an individual subject and MNI space, 102 | % the T1 from that subject has been nonlinearly warped to an MNI template. 103 | % Furthermore, a version of each subject's T1 that reflects this warping (and 104 | % resampling) has been saved. Let's use ITK-SNAP to quickly look at the MNI 105 | % template and each individual subject's warped T1 volume. 106 | % cd ~/nsd/nsddata/ 107 | % itksnap -g templates/MNI152_T1_1mm.nii.gz -o ppdata/subj*/anat/T1_to_MNI.nii.gz 108 | % How good is the alignment? 109 | %% 110 | 111 | 112 | 113 | %% Inspect NSD functional data (func1pt8mm) 114 | 115 | % The prepared NSD data come in 2 functional spaces (1.8 mm, 1.0 mm). 116 | % These spaces are aligned to each other, but have slightly different field-of-views 117 | % and origins (see 'nsddata description' for details). Note that the functional spaces 118 | % are not the same as the anatomical spaces. 119 | 120 | % Load floc_facestval.nii.gz (t-value for the contrast of faces vs. non-faces from 121 | % the floc category localizer experiment) and overlay it on mean.nii.gz. 122 | %% 123 | 124 | % Alternatively, overlay the t-values on T1_to_func1pt8mm.nii.gz (this is a 125 | % version of the T1 that has already been warped to the func1pt8mm space). 126 | %% 127 | 128 | % Explore and visualize some additional volumes: 129 | % - R2 (a measure of signal quality from the NSD experiment) 130 | % - prf_eccentricity (estimate of pRF eccentricity from the prf experiment) 131 | % - valid (fraction of NSD scan sessions in which valid data were recorded) 132 | % - R2_session??.nii.gz (R2 from individual NSD scan sessions) 133 | % Hint: itksnap -g mean.nii.gz -o R2_*.nii.gz 134 | %% 135 | 136 | 137 | 138 | %% Use freeview to explore surface-format data 139 | 140 | % Compared to volume data, surface data are a bit trickier to 141 | % visualize and manage. We will use FreeSurfer's freeview as 142 | % a simple surface viewer, though there are many alternatives. 143 | 144 | % Load subj01's left hemisphere inflated surface: 145 | % ~/nsd/nsddata/freesurfer/subj01/surf/lh.inflated 146 | %% 147 | 148 | % Play around in freeview and know how to: 149 | % - Navigate / Rotate / Zoom / Pan 150 | %% 151 | 152 | % Load as an overlay (generic): 153 | % ~/nsd/nsddata/freesurfer/subj01/label/lh.flocfacestval.mgz 154 | %% 155 | 156 | % Play around and know how to: 157 | % - Change the colormap and color range 158 | %% 159 | 160 | % Set the Render to 'Surface & mesh' to see the faces and vertices 161 | % that comprise a surface. 162 | %% 163 | 164 | % Set the Render back to Surface and load in: 165 | % ~/nsd/nsddata/freesurfer/subj01/label/lh.visualsulc.mgz 166 | % Develop a good visualization of this atlas. 167 | % Save a screenshot. 168 | %% 169 | 170 | % Now visualize the same data (visualsulc) on some 171 | % other versions of the cortical surface: 172 | % lh.white, lh.pial, lh.sphere 173 | % Use the same color settings as in the lh.inflated case and 174 | % save some screenshots to facilitate comparison. 175 | %% 176 | 177 | % Finally, to illustrate the folding-based registration 178 | % that underlies FreeSurfer's fsaverage surface, take 179 | % screenshots of the following (be careful to keep 180 | % the camera view fixed): 181 | % (1) subj01's lh.sphere - curvature 182 | % (2) subj01's lh.sphere - Kastner2015 183 | % (3) subj01's lh.sphere.reg - curvature 184 | % (4) subj01's lh.sphere.reg - Kastner2015 185 | % (5) fsaverage's lh.sphere - curvature 186 | % (6) fsaverage's lh.sphere - Kastner2015 187 | % Compare these screenshots and notice how sphere.reg is a 188 | % surface such that visualizing subj01's curvature on that surface 189 | % yields a map that looks well matched to fsaverage's curvature. 190 | %% 191 | 192 | % Like MNI, fsaverage is a common space that can be used to 193 | % compare results across subjects. MNI is a volume-based space 194 | % that is semi-accurate for cortex and is most accurate for 195 | % subcortical structures. In contrast, fsaverage is a surface-based 196 | % space that is applicable only to cortex. 197 | 198 | 199 | -------------------------------------------------------------------------------- /matlab/example02_loaddata.m: -------------------------------------------------------------------------------- 1 | %% Example 2: Loading data into MATLAB 2 | 3 | %% Introduction 4 | 5 | % In this script, we are going to go through examples of how 6 | % to load various types of data files into MATLAB. Loading data 7 | % is the first step before we can do data analysis. 8 | % 9 | % Skills/concepts: 10 | % - File formats 11 | % - Thinking about data formats (e.g. double, single, int16) 12 | % - Issues specific to NIFTI files 13 | % - Issues specific to surfaces 14 | % - HDF5-related concepts 15 | 16 | 17 | 18 | %% Miscellaneous data (.mat format) 19 | 20 | % MATLAB's .mat format is a catch-all format that can hold 21 | % all sorts of different kinds of data. 22 | 23 | % Load information pertaining to the experimental 24 | % design of the NSD experiment. By assigning the output 25 | % of load.m to a variable, we encapsulate the loaded variables 26 | % into a struct (thus, leaving the workspace uncluttered). 27 | a1 = load('~/nsd/nsddata/experiments/nsd/nsd_expdesign.mat'); 28 | a1 29 | %% 30 | 31 | 32 | 33 | %% Text data (.tsv format) 34 | 35 | % Although inefficient in terms of disk space, it is sometimes 36 | % convenient to store data in text format. The main advantage is that text 37 | % is easy to open (e.g. using a text editor or a spreadsheet program). 38 | % 39 | % A common format is .tsv (tab-separated values), which is similar 40 | % to .csv (comma-separated values). 41 | % 42 | % Typical things to watch out for include: making sure that any descriptive header rows 43 | % are ignored or handled appropriately, making sure that missing data (blanks or NaNs) 44 | % are handled correctly, making sure that numerical precision is handled correctly, 45 | % and making sure that any beginning or ending blank lines are not erroneously 46 | % treated as data entries. 47 | 48 | % Load in motion parameters for one NSD run. Note that in this case, 49 | % the text file consists of purely numeric data and has no header rows; 50 | % thus, we can simply use load.m to get the data. 51 | file0 = '~/nsd/nsddata_timeseries/ppdata/subj01/func1pt8mm/motion/motion_session21_run10.tsv'; 52 | a1 = load(file0); 53 | size(a1) % 226 volumes x 6 motion parameters 54 | %% 55 | figure; plot(a1(:,1)); 56 | %% 57 | 58 | % Load in behavioral data for one subject. Here we use importdata.m, 59 | % which incorporates some assumptions and returns the data in a 60 | % somewhat structured format (which is nice). However, because of the 61 | % assumptions, it is always a good idea to check the validity of the results. 62 | file0 = '~/nsd/nsddata/ppdata/subj01/behav/responses.tsv'; 63 | a1 = importdata(file0); 64 | a1 65 | %% 66 | a1.colheaders 67 | %% 68 | 69 | % Let's use an alternative method to load in the behavioral data. 70 | % In this method, we will use low-level routines that provide greater 71 | % degree of control over the loading process. 72 | fid = fopen(file0); 73 | a2 = textscan(fid,repmat('%f',[1 19]),'Delimiter','\t','HeaderLines',1); 74 | fclose(fid); 75 | a2 76 | %% 77 | 78 | % Are the two loading schemes equivalent? 79 | isequal(a1.data,cat(2,a2{:})) 80 | %% 81 | 82 | % Let's try that again. The trick is to realize that NaN does 83 | % not equal NaN according to == or isequal.m, but does according 84 | % to isequalwithequalnans.m. (Be careful!) 85 | isequalwithequalnans(a1.data,cat(2,a2{:})) 86 | %% 87 | 88 | 89 | 90 | %% Volume data as NIFTI files (.nii format) 91 | 92 | % NIFTI is a common neuroimaging format that is used to store 3D 93 | % (e.g. X x Y x Z) and 4D (e.g. X x Y x Z x T) volumes. 94 | 95 | % Here, we will use a function from the NIfTI_20140122 toolbox 96 | % in order to load in a volume of t-values (faces vs. non-faces). 97 | a1 = load_untouch_nii('~/nsd/nsddata/ppdata/subj01/func1pt8mm/floc_facestval.nii.gz'); 98 | a1 99 | %% 100 | 101 | % Some header fields that may be of interest include: 102 | a1.hdr.dime.dim % the volume has matrix size 81 x 104 x 83 103 | %% 104 | a1.hdr.dime.pixdim % the voxel size is 1.8-mm isotropic; the temporal resolution is 1.333 s 105 | %% 106 | 107 | % Importantly, the actual data is stored in a1.img. 108 | % Notice that the data have been saved in single format 109 | % (which occupies half the space compared to double format). 110 | a1 111 | %% 112 | 113 | % Let's visualize one slice of the data 114 | figure; imagesc(a1.img(:,:,34),[0 10]); colormap(hot); colorbar; axis image; 115 | %% 116 | 117 | % As a matter of convention, all prepared NSD data are saved in LPI, 118 | % meaning that the first voxel is left, posterior, and inferior. 119 | % In the following animation, we can check that this is the case. 120 | if 0 121 | for p=1:size(a1.img,3), p 122 | imagesc(a1.img(:,:,p),[0 10]); 123 | colormap(hot); colorbar; 124 | axis image; 125 | pause; 126 | end 127 | end 128 | 129 | % One more example. Here, we load a volume of ROI labels for visual cortex. 130 | % These are in the 0.8-mm anatomical space (matrix size is 320 x 320 x 320). 131 | a1 = load_untouch_nii('~/nsd/nsddata/ppdata/subj01/anat/roi/lh.Kastner2015.nii.gz'); 132 | a1 133 | %% 134 | 135 | % Visualize and check that it makes sense. 136 | figure; imagesc(a1.img(:,:,150),[0 25]); colormap(jet); axis image; 137 | %% 138 | 139 | % You may also want to load the exact same file in ITK-SNAP to compare. 140 | 141 | 142 | 143 | %% Surface data as MGZ files (.mgz format) 144 | 145 | % FreeSurfer uses .mgh/.mgz format for its internal file storage. 146 | % Although this format can store both volume and surface data, the prepared 147 | % NSD data make use of the .mgz format just to store surface data. 148 | % 149 | % Surface data do not have an easy ordering scheme, so surface data 150 | % can essentially be thought of as a vector of values that correspond 151 | % to the vertices that make up a surface. FreeSurfer generally keeps 152 | % left and right hemispheres separate; hence, for a complete set of 153 | % surface data, we typically use two .mgz files (e.g. lh.XXX.mgz 154 | % and rh.XXX.mgz). 155 | 156 | % Load in curvature values for the left hemisphere of one subject. 157 | % We use cvnloadmgz.m (which is just a wrapper around load_mgh.m, 158 | % a function provided by FreeSurfer). 159 | a1 = cvnloadmgz('~/nsd/nsddata/freesurfer/subj01/surf/lh.curvature.mgz'); 160 | size(a1) % vertices x 1 161 | %% 162 | figure; hist(a1(:),100); % gyri are < 0; sulci are > 0 163 | %% 164 | 165 | % Load in the Kastner2015 ROI labeling for the left hemisphere 166 | % of the fsaverage surface. Notice that the number of vertices in the 167 | % fsaverage surface is special and canonical. 168 | a1 = cvnloadmgz('~/nsd/nsddata/freesurfer/fsaverage/label/lh.Kastner2015.mgz'); 169 | size(a1) % vertices x 1 170 | %% 171 | union(a1(:),[])' % values are integers 0-25 172 | %% 173 | 174 | 175 | 176 | %% Betas as HDF5 files (.hdf5 format) 177 | 178 | % In the prepared NSD data, the primary data of interest are the beta weights 179 | % (fMRI response amplitudes) obtained for each trial. These betas are provided 180 | % in both NIFTI format as well as HDF5 format. The primary advantage of 181 | % HDF5 format is that it supports random access, and therefore can greatly 182 | % speed up loading of the data. 183 | 184 | % Use h5read.m to load in betas from the 15th NSD scan session. There are 750 185 | % trials in each session, and so we will be loading in 750 betas. 186 | % We load in data for only 5 voxels: we start at voxel (10,10,10) and load 187 | % in voxels 10 through 14 along the 3rd dimension. 188 | file0 = '~/nsd/nsddata_betas/ppdata/subj01/func1pt8mm/betas_fithrf/betas_session15.mat'; 189 | data = h5read(file0,'/betas',[10 10 10 1],[1 1 5 750]); 190 | size(data) 191 | %% 192 | class(data) % note that the data are stored in int16 format 193 | %% 194 | 195 | % Alternatively, we can use matfile.m as the loading mechanism. Compared to load.m, 196 | % the primary draw of matfile.m is that variables are not actually loaded 197 | % from disk until you specifically request them. In addition, matfile.m 198 | % supports random access (like h5read.m). In the code below, notice that we explicitly 199 | % cast the data to double format and then divide by 300 in order to convert 200 | % the betas to units of percent signal change. 201 | data = []; % 10 x 10 x 10 x 750 trials x 40 sessions 202 | for p=1:40, p 203 | file0 = sprintf('~/nsd/nsddata_betas/ppdata/subj01/func1pt8mm/betas_fithrf/betas_session%02d.mat',p); 204 | a1 = matfile(file0); 205 | tic; 206 | data(:,:,:,:,p) = double(a1.betas(41:50,3:12,35:44,:))/300; 207 | toc; 208 | end 209 | %% 210 | size(data) 211 | %% 212 | 213 | % Let's average the betas across trials and sessions, and then look at the result. 214 | temp = mean(mean(data,4),5); 215 | figure; imagesc(makeimagestack(temp),[-10 10]); axis image; colormap(cmapsign4); colorbar; 216 | %% 217 | 218 | % We might want to look at the results using ITK-SNAP, which might be more intuitive/easy. 219 | % To do that, we need to write the result to a NIFTI file. 220 | nsd_savenifti(temp,[1.8 1.8 1.8],'test.nii.gz'); 221 | 222 | % Load the test.nii.gz file into ITK-SNAP and see if you can convince yourself 223 | % that the two visualizations are consistent. Note that the function makeimagestack.m 224 | % extracts slices from the third dimension and places successive images downwards 225 | % and then rightwards. In addition, remember that the NSD volumes are saved in LPI 226 | % order. Thus, the orientation of the slices from makeimagestack.m do not match 227 | % the orientation of the first panel in ITK-SNAP (which is in "AR" ordering). 228 | % To make the figure from makeimagestack.m match ITK-SNAP, one can do the following: 229 | fun = @(x) flipdim(flipdim(permute(x,[2 1 3]),1),2); 230 | figure; imagesc(makeimagestack(fun(temp)),[-10 10]); axis image; colormap(cmapsign4); colorbar; 231 | %% 232 | 233 | 234 | 235 | %% Images as PNG files (.png format) 236 | 237 | % The .png file format is a lossless image format that is commonly 238 | % used for everyday computer tasks. It is convenient because your OS 239 | % can probably just open it and view it. However, the .png format does 240 | % not store multiple images, and having to keep track of large numbers 241 | % of files can cause severe slowdowns. 242 | 243 | % Load in one specific image 244 | file0 = '~/nsd/nsddata/stimuli/nsd/shared1000/shared0001_nsd02951.png'; 245 | im = imread(file0); % 425 pixels x 425 pixels x 3 (uint8 format) 246 | size(im) 247 | %% 248 | figure; imshow(im); 249 | %% 250 | class(im) 251 | %% 252 | 253 | % Load in many images 254 | stimfiles = matchfiles('~/nsd/nsddata/stimuli/nsd/shared1000/*.png'); 255 | im = zeros(425,425,3,1000,'uint8'); 256 | for p=1:length(stimfiles) 257 | statusdots(p,length(stimfiles)); 258 | im(:,:,:,p) = imread(stimfiles{p}); 259 | end 260 | size(im) 261 | %% 262 | 263 | 264 | 265 | %% Images in HDF5 format (.hdf5 format) 266 | 267 | % The NSD experiment involves a large number of images (73,000). We elected 268 | % to store these images in uint8 format in a single, very large .hdf5 file 269 | % in order to facilitate access. 270 | 271 | % Load in the 2951st NSD image 272 | stimfile = '~/nsd/nsddata_stimuli/stimuli/nsd/nsd_stimuli.hdf5'; 273 | im = h5read(stimfile,'/imgBrick',[1 1 1 2951],[3 425 425 1]); 274 | im = permute(im,[3 2 1]); % 425 pixels x 425 pixels x 3 (uint8 format) 275 | figure; imshow(im); 276 | %% 277 | class(im) 278 | %% 279 | 280 | 281 | 282 | %% Cortical surfaces in native FreeSurfer format (lh.white, lh.pial, etc.) 283 | 284 | % Use freesurfer_read_surf_kj.m (which is just a wrapper around 285 | % the freesurfer_read_surf.m function provided by FreeSurfer) to 286 | % read in the left hemisphere white-matter surface of one subject. 287 | [vertices,faces] = freesurfer_read_surf_kj('~/nsd/nsddata/freesurfer/subj01/surf/lh.white'); 288 | size(vertices) % vertices x 3 coordinates 289 | %% 290 | size(faces) % faces x 3 vertex indices 291 | %% 292 | 293 | % Let's also load in the inflated version of this surface. 294 | [vertices2,faces2] = freesurfer_read_surf_kj('~/nsd/nsddata/freesurfer/subj01/surf/lh.inflated'); 295 | 296 | % Notice that the white and inflated surfaces have exactly the same number of vertices 297 | isequal(size(vertices),size(vertices2)) 298 | %% 299 | 300 | % Also, the white and inflated surfaces have the exact same faces 301 | isequal(faces,faces2) 302 | %% 303 | 304 | % We can also use cvncode's cvnreadsurface.m to do the loading (which 305 | % is yet another wrapper). Note that the function relies on having the 306 | % FreeSurfer environment variable SUBJECTS_DIR correctly set. 307 | % (That is how it knows where to find the surface files.) 308 | getenv('SUBJECTS_DIR') 309 | %% 310 | surfL = cvnreadsurface('subj01','lh','inflated',[]); 311 | surfL 312 | %% 313 | 314 | 315 | 316 | %% Surface-based ROI files (various formats such as .mgz, .annot, .label) 317 | 318 | % FreeSurfer outputs a variety of different ROI files; in addition, 319 | % the prepared NSD data includes a number of ROIs. To facilitate access 320 | % to these ROIs, we can use the function cvnroimask.m. 321 | 322 | % Load in the Kastner 2015 atlas that has been prepared and mapped onto 323 | % subj01's cortical surface. Here we load in ROI labelings for the left hemisphere. 324 | [roimask,roidescription] = cvnroimask('subj01','lh','Kastner2015'); 325 | 326 | % There are 25 ROIs in the atlas; each element of roimask is a binary vector. 327 | roimask 328 | %% 329 | 330 | % A set of names are available too. 331 | roidescription 332 | %% 333 | 334 | % The names can also be read using this alternative method: 335 | file0 = '~/nsd/nsddata/freesurfer/subj01/label/Kastner2015.mgz.ctab'; 336 | roidescription2 = read_ctab(file0); 337 | roidescription2 338 | %% 339 | 340 | % Or directly as a text file: 341 | txt = loadtext(file0); 342 | txt 343 | %% 344 | 345 | 346 | -------------------------------------------------------------------------------- /matlab/example03_understandspaces.m: -------------------------------------------------------------------------------- 1 | %% Example 3: Understand mapping between spaces 2 | 3 | %% Introduction 4 | 5 | % nsd_mapdata.m is a utility function provided with NSD 6 | % that performs mapping between various NSD spaces (e.g. functional, 7 | % anatomical, surface). Please see nsdcode/matlab/examples_nsdmapdata.m 8 | % for a comprehensive example of how to use that function. 9 | % 10 | % Skills/concepts: 11 | % - How to use nsd_mapdata.m 12 | % - Interpolation/resampling 13 | % - Mapping between volume and surface 14 | % - Mapping between individual subjects and fsaverage 15 | 16 | 17 | -------------------------------------------------------------------------------- /matlab/example04_surfacevisualization.m: -------------------------------------------------------------------------------- 1 | %% Example 4: Automated surface visualization 2 | 3 | %% Introduction 4 | 5 | % cvnlookup.m is a function in the cvncode repository that enables 6 | % automated surface visualization (no manual intervention). This is 7 | % useful for large-scale analyses where manual intervention would 8 | % be prohibitively slow. Below, we provide some simple examples 9 | % of how to use the function. These examples include how to use 10 | % the function to draw an ROI on a surface. 11 | % 12 | % There are many different tools that can visualize surfaces, and 13 | % we encourage the user to decide what is best for their needs. 14 | % The major disadvantage of cvnlookup.m is that it has no GUI and 15 | % is not designed for interactive use (e.g. rotating/panning/zooming). 16 | % 17 | % Skills/concepts: 18 | % - How to use cvnlookup.m 19 | % - How to draw ROIs on surfaces 20 | % - Issues when mapping volume to surface 21 | 22 | 23 | 24 | %% Example use of cvnlookup.m 25 | 26 | % Note that a pre-requisite for using cvnlookup.m is to have your 27 | % FreeSurfer SUBJECTS_DIR environment variable set correctly. 28 | getenv('SUBJECTS_DIR') 29 | %% 30 | 31 | % Let's visualize the Kastner2015 atlas on subj01 32 | data = '~/nsd/nsddata/freesurfer/subj01/label/lh.Kastner2015.mgz'; 33 | cvnlookup('subj01',1,data,[0 25],jet(256),0.5); 34 | %% 35 | 36 | % Note that we automatically get some variables (rawimg,Lookup,rgbimg,himg) 37 | % assigned to the workspace (see 'help cvnlookup'). 38 | 39 | % We can directly call imagesc on rawimg. This might be useful 40 | % for quickly playing around with colormaps and color ranges. 41 | figure; imagesc(rawimg,[0 25]); colormap(jet(256)); axis image; 42 | %% 43 | 44 | 45 | 46 | %% Draw an ROI 47 | 48 | % Let's call cvnlookup again and explicitly get the outputs. 49 | % In this call, we will threshold away all of the values, 50 | % leaving just the curvature showing. 51 | [rawimg,Lookup,rgbimg,himg] = cvnlookup('subj01',1,data,[0 25],jet(256),25.5); 52 | %% 53 | 54 | % Manually draw an ROI on the left hemisphere near the tip of the calcarine sulcus. 55 | % Note: drawroipoly.m is valid only on spherical surfaces. 56 | if 0 57 | Rmask = drawroipoly(himg,Lookup); 58 | end 59 | 60 | % To allow automated execution, we embed the above code in an if-statement 61 | % and provide the following hard-coded values: 62 | Rmask = zeros(453622,1); 63 | Rmask([3635 3653 3654 3666 4269 4285 4286 4305 4306 4318 4319 4338 4339 4358 4375 4393 4408 4994 4995 5009 5010 5027 5028 5044 5045 5066 5076 5092 5107 5120 5708 5709 5710 5719 5720 5721 5733 5734 5735 5749 5750 5770 5791 5805 5806 5825 5826 5841 5842 5856 5857 5869 5870 6493 6503 6504 6515 6516 6534 6535 6557 6558 6571 6572 6594 6595 6616 6632 6647 7353 7354 7355 7366 7367 7387 7388 7410 7423 7441 7455 7469 7481 8173 8174 8188 8213 8214 8231 8232 8249 8269 8285 8286 8295 8296 8306 8307 8317 8318 8998 9019 9046 9062 9063 9084 9108 9128 9141 9151 9159]) = 1; 64 | 65 | % Rmask is a (numlh+numrh)x1 binary mask 66 | size(Rmask) 67 | %% 68 | 69 | % Let's visualize the ROI in order to check it 70 | extraopts = {'roicolor','k','roimask',Rmask}; 71 | cvnlookup('subj01',1,data,[0 25],jet(256),0.5,[],[],extraopts); 72 | %% 73 | 74 | % We can also directly visualize the ROI as if it were data 75 | cvnlookup('subj01',1,Rmask,[0 1],gray); 76 | %% 77 | 78 | 79 | 80 | %% Save the ROI 81 | 82 | % This is a little tricky because the ROI data reflects both hemispheres concatenated, 83 | % whereas we typically want to save .mgz files that reflect one hemisphere at a time. 84 | 85 | % First, create a valstruct and put the ROI inside it 86 | valstruct = valstruct_create('subj01'); 87 | valstruct = setfield(valstruct,'data',Rmask); 88 | valstruct 89 | %% 90 | 91 | % Now, we can save an .mgz file 92 | fsdir = '~/nsd/nsddata/freesurfer/subj01'; 93 | nsd_savemgz(valstruct_getdata(valstruct,'lh'),'lh.testA.mgz',fsdir); 94 | 95 | % Note that lh.testA.mgz can be fed back as data into cvnlookup (even though it is just 96 | % one hemisphere, the code automatically fills in NaNs for the other hemisphere). 97 | cvnlookup('subj01',1,'lh.testA.mgz',[0 1],gray,0.5); 98 | %% 99 | 100 | 101 | 102 | %% Example of automated surface visualization 103 | 104 | % We will load in each subject's curvature values, map them (via nearest-neighbor) 105 | % to fsaverage, and then generate a visualization of the results. 106 | 107 | % define 108 | subjs = {'subj01' 'subj02' 'subj03' 'subj04' 'subj05' 'subj06' 'subj07' 'subj08' 'fsaverage'}; 109 | hemis = {'lh' 'rh'}; 110 | 111 | % do it 112 | for p=1:length(subjs) 113 | subjectid = subjs{p}; 114 | 115 | % for individual subjects, we have to map the curvature values to fsaverage 116 | if p <= 8 117 | 118 | % loop over hemispheres 119 | data = []; 120 | for h=1:length(hemis) 121 | inputfile = sprintf('~/nsd/nsddata/freesurfer/%s/surf/%s.curvature.mgz',subjectid,hemis{h}); 122 | data = [data; nsd_mapdata(p,sprintf('%s.white',hemis{h}),'fsaverage',inputfile)]; 123 | end 124 | 125 | % for fsaverage, we just have to load in the curvature values 126 | else 127 | 128 | % load the curvature values from both hemispheres 129 | inputfile = sprintf('~/nsd/nsddata/freesurfer/%s/surf/??.curvature.mgz',subjectid); 130 | data = cvnloadmgz(inputfile); 131 | 132 | end 133 | 134 | % perform lookup and save to .png file 135 | outputfile = sprintf('curvature_%s.png',subjectid); 136 | [rawimg,Lookup,rgbimg,himg] = cvnlookup('fsaverage',1,data < 0,[-1 2],gray(256),[],[],0); 137 | imwrite(rgbimg,outputfile); 138 | 139 | end 140 | 141 | % visualize 142 | figure; imshow('curvature_subj01.png'); 143 | %% 144 | 145 | % Review all of the .png files that are written out. Confirm that the 146 | % curvature-based alignment of the different subjects to fsaverage is 147 | % of reasonable quality. An effective method for this is to quickly 148 | % flip between the various figures. 149 | %% 150 | 151 | 152 | 153 | %% Example of surface visualization from volume data 154 | 155 | % The official spaces for the prepared NSD betas are the functional 156 | % 1.8-mm space and the functional 1.0-mm space. This has the 157 | % consequence that if analyses are performed for the data in these 158 | % spaces, in order to generate surface visualizations of the results, 159 | % one must map from the functional volume spaces to the cortical 160 | % surface. Given the high-resolution of the functional data, we 161 | % prepare 3 different cortical surfaces (layerB1, layerB2, layerB3) 162 | % that exist at different depths through the gray matter. Here, 163 | % we show an example of how one might map from the functional volume 164 | % spaces to the cortical surface so that a surface visualization can 165 | % be made. 166 | % 167 | % Note that some provided data files are already mapped to the cortical 168 | % surface of each subject; for such data, there is no need to map to the surface. 169 | 170 | % define 171 | hemis = {'lh' 'rh'}; 172 | subjix = 1; 173 | file0 = '~/nsd/nsddata/ppdata/subj01/func1pt8mm/prf_eccentricity.nii.gz'; 174 | 175 | % load data 176 | a1 = load_untouch_nii(file0); 177 | 178 | % map to the three depth surfaces using cubic interpolation 179 | tdata = {}; 180 | for q=1:length(hemis) 181 | for d=1:3 182 | tdata{q}(:,d) = nsd_mapdata(subjix,'func1pt8',sprintf('%s.layerB%d',hemis{q},d),a1.img,'cubic',[],[],[],[]); 183 | end 184 | end 185 | 186 | % average across depth and concatenate into a vector 187 | data = cat(1,mean(tdata{1},2),mean(tdata{2},2)); 188 | 189 | % generate surface visualization 190 | cvnlookup(sprintf('subj%02d',subjix),1,data,[0 5],jet(256)); 191 | %% 192 | 193 | % There are some issues that should be considered when performing the mapping 194 | % from volume to surface. One issue is the choice of interpolation (e.g. nearest vs. 195 | % linear vs. cubic), as this choice will indeed affect the results. Keep in mind that 196 | % different types of data may be more or less suitable for interpolation. For example, 197 | % circular quantities will need to be specially handled. Another issue is how 198 | % cortical depth is handled. In the example above, we simply average across 199 | % the three depths, but the user may want to handle this differently. A third issue 200 | % is that one needs to be careful about any potential non-finite values in the data 201 | % volume being mapped. Such values may propagate depending on the interpolation choice. 202 | % A fourth issue is that the mapping from volume to surface is in general a lossy 203 | % process (i.e. interpolation involves data modification and potential resolution loss), 204 | % so one should be careful in that regard. 205 | 206 | 207 | -------------------------------------------------------------------------------- /matlab/example05_inspectdataatscale.m: -------------------------------------------------------------------------------- 1 | %% Example 5: Inspect data at scale 2 | 3 | %% Introduction 4 | 5 | % In this script, we demonstrate two different ways to quickly and effectively 6 | % inspect large amounts of data. The first way is to use a high framerate 7 | % movie, thus exploiting the rapidity of our visual system. The second way is 8 | % to use an image to visualize many time series at once. 9 | % 10 | % Skills/concepts: 11 | % - Manipulation of images, volumes, and movies 12 | % - Image normalization and color ranges 13 | % - aseg (from FreeSurfer) 14 | 15 | 16 | 17 | %% Make a movie of brain volumes over time 18 | 19 | % In this example, we will inspect all of the pre-processed fMRI time-series data 20 | % from the first NSD session from subj01. Knowing how to use low-level routines 21 | % to create the movie is useful as it allows you to customize the movie to your needs. 22 | 23 | % define 24 | files = '~/nsd/nsddata_timeseries/ppdata/subj01/func1pt8mm/timeseries/timeseries_session01_*.nii.gz'; 25 | outfile = 'testmovie'; % we will add file extensions later 26 | fps = 72; % frames per second for the movie 27 | 28 | % find the files 29 | files0 = matchfiles(files); 30 | 31 | % load the data from each run and construct orthographic image inspections 32 | im = uint8([]); 33 | cnt = 1; 34 | for p=1:length(files0) 35 | 36 | % load data and convert to double 37 | data0 = load_untouch_nii(files0{p}); 38 | data0 = double(data0.img); 39 | 40 | % for each volume, make an image 41 | for r=1:size(data0,4) 42 | 43 | % pull out middle slice in all three dimensions 44 | imA = []; % 120 x 120 x 3 45 | imA = cat(3,imA,placematrix(zeros(120,120), squeeze(data0(:,:,round(end/2),r)))); 46 | imA = cat(3,imA,placematrix(zeros(120,120),rotatematrix(squeeze(data0(:,round(end/2),:,r)),1,2,1))); 47 | imA = cat(3,imA,placematrix(zeros(120,120),rotatematrix(squeeze(data0(round(end/2),:,:,r)),1,2,1))); 48 | 49 | % normalize, make a mosaic, and convert to uint8 50 | im(:,:,cnt) = uint8(255*makeimagestack(imA,[0 2000],1,[1 3])); 51 | cnt = cnt + 1; 52 | 53 | % the first ten volumes of each run get marked by a little white square 54 | if r <= 10 55 | im(1:4,1:4,end) = 255; 56 | end 57 | 58 | end 59 | 60 | end 61 | 62 | % visualize one image 63 | figure; imshow(im(:,:,1)); 64 | %% 65 | 66 | % write all of the images to a single .mov file 67 | imagesequencetomovie(im,sprintf('%s.mov',outfile),fps); 68 | 69 | % make a compressed version of the movie (mpeg-4 format) 70 | unix_wrapper(sprintf('HandBrakeCLI -i %s.mov -q 2 --strict-anamorphic -o %s.m4v',outfile,outfile)); 71 | 72 | % note that instead of QTWriter (used by imagesequencetomovie.m), we could perhaps use 73 | % other utilities such as built-in MATLAB toolbox functions and/or ffmpeg. 74 | 75 | 76 | 77 | %% Use a carpet plot (Power NeuroImage 2017) to quickly inspect time-series data 78 | 79 | % In this example, we will use a carpet plot to visualize the same data as above. 80 | 81 | % define 82 | files = '~/nsd/nsddata_timeseries/ppdata/subj01/func1pt8mm/timeseries/timeseries_session01_*.nii.gz'; 83 | asegfile = '~/nsd/nsddata/ppdata/subj01/func1pt8mm/aseg.nii.gz'; 84 | 85 | % define more 86 | grayix = [42 3]; % gray matter (rh is 42, lh is 3) 87 | wmix = [41 2]; % white matter (rh is 41, lh is 2) 88 | csfix = 24; % CSF 89 | maxplot = 500; % maximum number of voxels to plot from each compartment 90 | 91 | % use aseg to decide which voxels to extract 92 | aseg = load_untouch_nii(asegfile); 93 | grayvx = picksubset(find(ismember(aseg.img,grayix)),maxplot); 94 | wmvx = picksubset(find(ismember(aseg.img,wmix)),maxplot); 95 | csfvx = picksubset(find(ismember(aseg.img,csfix)),maxplot); 96 | 97 | % calculate row bounds 98 | rowbounds = [length(grayvx) length(wmvx) length(csfvx)]; 99 | rowbounds = cumsum(rowbounds); 100 | 101 | % find the time-series files 102 | files0 = matchfiles(files); 103 | 104 | % for each file, collect the data 105 | colbounds = []; 106 | alldata = []; 107 | for p=1:length(files0) 108 | 109 | % load data, convert to double, flatten voxels 110 | data0 = load_untouch_nii(files0{p}); 111 | data0 = squish(double(data0.img),3); 112 | 113 | % extract data and z-score each voxel 114 | temp = cat(1,data0(grayvx,:),data0(wmvx,:),data0(csfvx,:)); 115 | temp = calczscore(temp,2); % there are other ways we could normalize... 116 | 117 | % record 118 | alldata = cat(2,alldata,temp); 119 | 120 | % calculate column bounds 121 | colbounds = [colbounds size(data0,2)]; 122 | 123 | end 124 | colbounds = cumsum(colbounds); 125 | 126 | % make the visualization 127 | figureprep([100 100 800 600],1); hold on; 128 | imagesc(alldata,[-3 3]); 129 | colormap(gray); 130 | axis ij; 131 | axis([.5 size(alldata,2)+.5 .5 size(alldata,1)+.5]); 132 | straightline(rowbounds+.5,'h','r-'); 133 | straightline(colbounds+.5,'v','r-'); 134 | xlabel('Volume number'); 135 | ylabel('Voxel'); 136 | %% 137 | 138 | % In the plot, the three row compartments are gray matter, white matter, and CSF, 139 | % and the column compartments correspond to different fMRI runs. 140 | 141 | 142 | -------------------------------------------------------------------------------- /matlab/example06_basicbetaloading.m: -------------------------------------------------------------------------------- 1 | %% Example 6: Basic loading and inspection of NSD betas 2 | 3 | %% Introduction 4 | 5 | % In this script, we go through an example of loading and inspecting the NSD betas. 6 | % 7 | % Skills/concepts: 8 | % - Thinking about data units 9 | % - Data visualization 10 | % - Simple ROI definition 11 | % - Non-trivial indexing into the NSD data 12 | 13 | 14 | 15 | %% General setup 16 | 17 | % define 18 | stimfile = '~/nsd/nsddata_stimuli/stimuli/nsd/nsd_stimuli.hdf5'; 19 | expfile = '~/nsd/nsddata/experiments/nsd/nsd_expdesign.mat'; 20 | subjix = 3; % which NSD subject we are analyzing 21 | nsess = 32; % how many NSD sessions are available 22 | betaver = 'betas_fithrf'; % which beta version to load 23 | 24 | 25 | 26 | %% Define ROI 27 | 28 | % We are going to examine NSD betas for one region of interest (ROI). 29 | % Specifically, right hemisphere (RH) fusiform face area (FFA). 30 | % Here, we go through one approach for defining that region. 31 | 32 | % load in t-values for faces vs. nonfaces from the floc experiment 33 | a1 = load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/floc_facestval.nii.gz',subjix)); 34 | 35 | % in ITK-SNAP, look at the NIFTI file and determine a small box in 36 | % the appropriate anatomical location and containing high t-values (e.g. > 5) 37 | lrix = 58:66; % from left to right 38 | paix = 31:54; % from posterior to anterior 39 | isix = 25:32; % from inferior to superior 40 | 41 | % note that this a rough-and-ready approach to defining the ROI. it is 42 | % sufficient for the sake of this example, but a more accurate approach 43 | % would be to examine the data on the cortical surface. 44 | 45 | % create a binary volume with the box 46 | boxvol = zeros(size(a1.img)); 47 | boxvol(lrix,paix,isix) = 1; 48 | 49 | % define the ROI as within the box AND t > 5 50 | mask = boxvol & a1.img > 5; 51 | 52 | % to aid in loading the data, compute a tight fitting box and associated indices 53 | [d1,d2,d3,ii] = computebrickandindices(mask); 54 | 55 | 56 | 57 | %% Load in the betas 58 | 59 | % load data 60 | data = []; % voxels x 750 trials x sessions 61 | for p=1:nsess 62 | fprintf('sess %d...',p); 63 | file0 = sprintf('~/nsd/nsddata_betas/ppdata/subj%02d/func1pt8mm/%s/betas_session%02d.mat',subjix,betaver,p); 64 | a1 = matfile(file0); 65 | temp = double(a1.betas(d1,d2,d3,:))/300; % convert to double and then convert to percent signal change 66 | temp = squish(temp,3); % flatten voxels 67 | temp = temp(ii,:); % extract the voxels we want 68 | data(:,:,p) = temp; % record 69 | end 70 | 71 | 72 | 73 | %% Inspect the data 74 | 75 | % visualize data in original units (percent signal change) 76 | figure; hold on; 77 | imagesc(reshape(data,size(data,1),[]),[-10 10]); 78 | axis ij tight; 79 | colormap(cmapsign4); colorbar; 80 | xlabel('Trial'); 81 | ylabel('Voxel'); 82 | title('Response (% BOLD)'); 83 | %% 84 | 85 | % Notice the high heterogeneity across voxels. 86 | %% 87 | 88 | % visualize each voxel's mean PSC 89 | figure; hold on; 90 | bar(mean(mean(data,2),3)); 91 | xlabel('Voxel'); 92 | ylabel('Average response (% BOLD)'); 93 | %% 94 | 95 | % This reinforces the point: voxels have highly different overall BOLD responses. 96 | %% 97 | 98 | % on a per-voxel basis, z-score the betas obtained in each session. 99 | % this is a fairly drastic measure, but ensures stationarity across time 100 | % and comparable units across voxels. 101 | dataZ = calczscore(data,2); 102 | 103 | % visualize data again in z-score units 104 | figure; hold on; 105 | imagesc(reshape(dataZ,size(dataZ,1),[]),[-3 3]); 106 | axis ij tight; 107 | colormap(cmapsign4); colorbar; 108 | xlabel('Trial'); 109 | ylabel('Voxel'); 110 | title('Response (z-score units)'); 111 | %% 112 | 113 | 114 | 115 | %% Load in experiment information 116 | 117 | % load 118 | exp1 = load(expfile); 119 | theorder = exp1.masterordering(1:750*nsess); % the trials that we have data for 120 | uniqueix = union(theorder,[]); % unique indices into the 10k 121 | length(theorder) % total number of trials 122 | length(uniqueix) % total number of unique images 123 | %% 124 | 125 | 126 | 127 | %% Visualize ROI-averaged responses 128 | 129 | % define 130 | numtodo = 100; % number of distinct images to plot responses for 131 | 132 | % massage dimensionality 133 | dataZ = reshape(dataZ,size(dataZ,1),[]); % voxels x trials*sessions 134 | 135 | % make the plot 136 | todo = picksubset(uniqueix,numtodo); % pick a small subset to plot 137 | versions = {'Regular' 'Shuffled'}; 138 | for ver=1:2 139 | figureprep([100 100 1000 300],1); hold on; 140 | avgresp = []; 141 | for p=1:length(todo) 142 | switch ver 143 | case 1 144 | ix = find(theorder==todo(p)); % which trials correspond to the image 145 | case 2 146 | ix = find(permutedim(theorder==todo(p))); % SHUFFLE! 147 | end 148 | yy = mean(dataZ(:,ix),1); % compute ROI average for each trial 149 | scatter(repmat(p,[1 length(yy)]),yy,'ro'); 150 | avgresp(p) = mean(yy); 151 | end 152 | h = plot(1:length(todo),avgresp,'ko-'); 153 | set(h,'MarkerFaceColor','k'); 154 | ax = axis; 155 | axis([ax(1:2) -2 2]); 156 | xlabel('Image'); 157 | ylabel('Response (z-score units)'); 158 | title(versions{ver}); 159 | end 160 | %% 161 | 162 | % In the 'Regular' plot, we are looking for small within-image variability 163 | % compared to the across-image variability. The 'Shuffled' plot looks slightly 164 | % different compared to the 'Regular' plot, indicating that the image-evoked 165 | % signal in the data is fairly weak. This is expected given the noise in fMRI 166 | % data, the fact that an aggressive rapid event-related design was used in NSD 167 | % (where responses to successive trials overlap substantially), and the small 168 | % number of trials per distinct image that was used in the design. 169 | 170 | 171 | 172 | %% Look at best and worst images 173 | 174 | % compute the mean across trials only for those images with all 3 trials 175 | newdata = []; % voxels x images with the trial-averaged response 176 | newdatastim = []; % 1 x images with indices into the 10k 177 | for p=1:length(uniqueix) 178 | ix = find(theorder==uniqueix(p)); 179 | if length(ix)==3 180 | newdata(:,end+1) = mean(dataZ(:,ix),2); 181 | newdatastim(end+1) = uniqueix(p); 182 | end 183 | end 184 | 185 | % sort ROI-averaged response in descending order 186 | [ss,ssix] = sort(mean(newdata,1),'descend'); 187 | 188 | % plot best and worst images 189 | strs = {'Best' 'Worst'}; 190 | for flag=1:2 191 | figureprep([100 100 1000 300],1); 192 | for p=1:5 193 | 194 | % figure out 73k ID 195 | switch flag 196 | case 1 197 | id73k = exp1.subjectim(subjix,newdatastim(ssix(p))); 198 | case 2 199 | id73k = exp1.subjectim(subjix,newdatastim(ssix(end-p+1))); 200 | end 201 | 202 | % get image (425 x 425 x 3, uint8) 203 | im = permute(h5read(stimfile,'/imgBrick',[1 1 1 id73k],[3 425 425 1]),[3 2 1]); 204 | 205 | % plot it 206 | subplot(1,5,p); 207 | imshow(im); 208 | if p==1 209 | title(strs{flag}); 210 | end 211 | 212 | end 213 | end 214 | %% 215 | 216 | % As expected, the images that most strongly drove the response tend to have faces. 217 | 218 | 219 | -------------------------------------------------------------------------------- /matlab/example07_simplecontrast.m: -------------------------------------------------------------------------------- 1 | %% Example 7: A simple contrast-based analysis of the NSD betas 2 | 3 | %% Introduction 4 | 5 | % In this script, we demonstrate a simple analysis of the NSD betas. The analysis 6 | % is based on the actual content of the NSD images --- specifically, we make use of 7 | % face annotations that were generated as part of "NSD_Annotation_Efforts_1.0", 8 | % which is an extension to the NSD data (it is not officially part of the 9 | % prepared NSD data). The analysis itself is very simple (essentially just a 10 | % t-test), but this script goes through a number of important points. 11 | % 12 | % Skills/concepts: 13 | % - Memory management 14 | % - Resampling strategies (split-half, shuffle/permutation) 15 | % - Using line plots to better understand cortical map visualizations 16 | 17 | 18 | 19 | %% General setup 20 | 21 | % define 22 | expfile = '~/nsd/nsddata/experiments/nsd/nsd_expdesign.mat'; 23 | nsess = 2; % how many of the first N NSD sessions to consider (must be >= 2) 24 | betaver = 'betas_fithrf'; % which beta version to load 25 | 26 | % load 27 | exp1 = load(expfile); 28 | 29 | 30 | 31 | %% Load face annotations 32 | 33 | % load automated outputs: simple face count 34 | a1 = load('~/Dropbox/nsdabudhabi/nsdextensions/NSD_Annotation_Efforts_1.0/Automated/Faces/regcount_mode1.mat'); 35 | 36 | 37 | 38 | %% Load betas prepared in fsaverage space and analyze them 39 | 40 | % The large scale of the NSD data comes with pain points. One is memory usage. 41 | % Unless you have large amounts of RAM, it may be impossible to hold all of 42 | % the relevant data in RAM at a single time. Thus, one may need to analyze 43 | % the data in chunks. In this script, we will process each subject one at a 44 | % time and only keep in RAM the relevant analysis outputs (not the data). 45 | % 46 | % Designing code to handle memory well is not trivial. One must have a birds-eye 47 | % perspective on the architecture of the analysis, the memory usage, and the 48 | % exact quantities that need to be computed. One wants to make sure that all 49 | % of the necessary analysis outputs are computed before one gets rid of the 50 | % data to make room for new edata. Since loading data from disk is expensive 51 | % in terms of execution time, ideally, code would be written correctly up front. 52 | 53 | % loop over subjects 54 | tvals = single([]); % vertices x 4 x subjects (full, odd, even, shuffle) 55 | mns = single([]); % vertices x 2 x subjects (face, non-face) 56 | ses = single([]); % vertices x 2 x subjects (face, non-face) 57 | for subjix=1:8, subjix 58 | 59 | % load in all of the betas 60 | alldata = single([]); % 327684 vertices x 750 trials x sessions 61 | for sess=1:nsess 62 | fprintf('sess%d...',sess); 63 | file0 = sprintf('~/nsd/nsddata_betas/ppdata/subj%02d/fsaverage/%s/*.betas_session%02d.*',subjix,betaver,sess); 64 | data = cvnloadmgz(file0); % 327684 x 1 x 1 x 750 65 | alldata(:,:,sess) = single(permute(data,[1 4 2 3])); % use single to save on memory 66 | clear data; 67 | end 68 | 69 | % at this point, the betas are in units of percent signal change. 70 | % to deal with gross session differences, you may want to consider 71 | % z-scoring the betas from each session (on a per-voxel basis). 72 | 73 | % prepare face counts (750 trials x sessions, non-negative integers) 74 | imageix = exp1.subjectim(subjix,exp1.masterordering(1:750*nsess)); % 1 x trials with 73k IDs 75 | counts = a1.countval(imageix); % 1 x trials with face counts 76 | counts = reshape(counts,750,[]); % 750 trials x sessions 77 | 78 | % compute t-value for faces vs. non-faces 79 | counts0 = counts; 80 | [mn1,se1] = meanandse(alldata(:,flatten(counts)>0),2); % at least one face 81 | [mn2,se2] = meanandse(alldata(:,flatten(counts)==0),2); % no faces 82 | tvals(:,1,subjix) = (mn1 - mn2) ./ sqrt(se1.^2 + se2.^2); 83 | % we also save the means and std errs for this case 84 | mns(:,1,subjix) = mn1; 85 | mns(:,2,subjix) = mn2; 86 | ses(:,1,subjix) = se1; 87 | ses(:,2,subjix) = se2; 88 | 89 | % compute t-value for odd sessions 90 | counts0 = counts; 91 | counts0(:,2:2:end) = NaN; % set even sessions to NaNs (the NaNs will not satisfy the logical operators) 92 | [mn1,se1] = meanandse(alldata(:,flatten(counts0)>0),2); % at least one face 93 | [mn2,se2] = meanandse(alldata(:,flatten(counts0)==0),2); % no faces 94 | tvals(:,2,subjix) = (mn1 - mn2) ./ sqrt(se1.^2 + se2.^2); 95 | 96 | % compute t-value for even sessions 97 | counts0 = counts; 98 | counts0(:,1:2:end) = NaN; % set odd sessions to NaNs (the NaNs will not satisfy the logical operators) 99 | [mn1,se1] = meanandse(alldata(:,flatten(counts0)>0),2); % at least one face 100 | [mn2,se2] = meanandse(alldata(:,flatten(counts0)==0),2); % no faces 101 | tvals(:,3,subjix) = (mn1 - mn2) ./ sqrt(se1.^2 + se2.^2); 102 | 103 | % shuffle labels and re-analyze 104 | for shuffle=1:1 % here, we do just one, but one can adapt this to do many 105 | counts0 = permutedim(counts,0); 106 | [mn1,se1] = meanandse(alldata(:,flatten(counts0)>0),2); % at least one face 107 | [mn2,se2] = meanandse(alldata(:,flatten(counts0)==0),2); % no faces 108 | tvals(:,3+shuffle,subjix) = (mn1 - mn2) ./ sqrt(se1.^2 + se2.^2); 109 | end 110 | 111 | end 112 | 113 | 114 | 115 | %% Generate cortical map visualizations 116 | 117 | % loop through each of the 4 analysis results (full, odd, even, shuffle) 118 | Lookup = []; 119 | titlestr = {'All' 'Odd' 'Even' 'Shuffle'}; 120 | for typ=1:size(tvals,2) 121 | 122 | figureprep([0 0 1000 700],1); 123 | for subjix=1:8 124 | 125 | % use ventral inflated view of fsaverage; range -10 to 10; 126 | % threshold values that are less than 3 and greater than -3 127 | [rawimg,Lookup,rgbimg] = cvnlookup('fsaverage',3,tvals(:,typ,subjix),[-10 10],cmapsign4(256),3*j,Lookup,0); 128 | 129 | % write image to disk 130 | imwrite(rgbimg,sprintf('facetval_typ%02d_subj%02d.png',typ,subjix)); 131 | 132 | % also place into figure window 133 | subplot(2,4,subjix); hold on; 134 | imshow(rgbimg); 135 | if subjix==1 136 | title(sprintf('%s; Subject %d',titlestr{typ},subjix)); 137 | else 138 | title(sprintf('Subject %d',subjix)); 139 | end 140 | 141 | end 142 | 143 | end 144 | %% 145 | 146 | % Notice that even though the subjects are in fsaverage space, there is 147 | % substantial variability in the location of face-selective cortex. 148 | %% 149 | 150 | % Notice that there is high consistency across the Odd and Even analyses. 151 | %% 152 | 153 | % Notice that the shuffle analysis produces very few statistically significant 154 | % regions (this is good). The null hypothesis embodied by this analysis is that 155 | % there is no relationship between the faces vs. non-faces categorization and the 156 | % brain data. The shuffling analysis has the nice property that it is closely 157 | % matched to the actual empirical data (it has the same exact numerical values; 158 | % the order is simply scrambled). 159 | % 160 | % The visualization is thresholded at |t|>3, and under this threshold, 161 | % there are still a few false positives that occur. Note that one could do 162 | % more extensive NHST quantification, such as performing many shuffles and 163 | % calculating some metric of the results (e.g. number of vertices that achieve 164 | % significance). Also, note that the issues here are closely related to 165 | % family-wise error rate and false discovery rate, but we won't go into 166 | % those issues here. 167 | %% 168 | 169 | 170 | 171 | %% Use line plots to better understand the cortical maps 172 | 173 | % plot the Full result, averaged across subjects 174 | [rawimg,Lookup,rgbimg] = cvnlookup('fsaverage',3,mean(tvals(:,1,:),3),[-10 10],cmapsign4(256),3*j,Lookup,0); 175 | 176 | % put up the image 177 | hmapfig = figure; 178 | himg = imshow(rgbimg); 179 | %% 180 | 181 | % draw a line on the right hemisphere from left to right, 182 | % through the middle of the large yellow region 183 | if 0 184 | % ask the user to draw a line (green=start, red=end) 185 | xypoints = []; % Note: can provide initial xypoints as Nx2 matrix of pixel coords 186 | [xypoints,xyline] = roiline(hmapfig,[],xypoints); 187 | 188 | % we now have: 189 | % xypoints = Nx2 pixel coords of line segment endpoints that the user defined/clicked (decimal) 190 | % xyline = Vx2 pixel coords of ALL pixel centers along the defined line segments (integer) 191 | end 192 | 193 | % to allow automated execution, we embed the above code in an if-statement 194 | % and provide the following hard-coded values: 195 | xypoints = [75.7499999999999 668.75;146.25 658.25]; 196 | xyline = [76 669;77 669;78 668;79 668;80 668;81 668;82 668;83 668;84 667;85 667;86 667;87 667;88 667;89 667;90 667;91 666;92 666;93 666;94 666;95 666;96 666;97 666;98 665;99 665;100 665;101 665;102 665;103 665;104 665;105 664;106 664;107 664;108 664;109 664;110 664;111 663;112 663;113 663;114 663;115 663;116 663;117 663;118 662;119 662;120 662;121 662;122 662;123 662;124 662;125 661;126 661;127 661;128 661;129 661;130 661;131 660;132 660;133 660;134 660;135 660;136 660;137 660;138 659;139 659;140 659;141 659;142 659;143 659;144 659;145 658]; 197 | 198 | % convert pixel coords (xyline) to vertex indices via Lookup. 199 | % each pixel center is mapped to a single surface vertex (via nearest-neighbor). 200 | % vertidx = Vx1 vertex indices (between 1 and numlh+numrh) 201 | vertidx = spherelookup_imagexy2vertidx(xyline,Lookup); 202 | 203 | % remove repeating vertices 204 | vertidxU = filterout(removerepeats(vertidx),NaN); 205 | 206 | % let's check the location of these vertices 207 | cvnlookup('fsaverage',3,copymatrix(zeros(size(tvals,1),1),vertidxU,1),[0 1],gray,0.5,Lookup); 208 | %% 209 | 210 | % plot some data values along the line (we call this a "line profile"). 211 | % keep in mind that the line is not necessarily exactly straight, given 212 | % that it is constrained to be a series of fsaverage vertices. 213 | subjix = 1; % for simplicity, we will show only subj01 214 | figure; 215 | subplot(2,1,1); hold on; 216 | plot(tvals(vertidxU,1,subjix),'ro-'); 217 | straightline(0,'h','k-'); 218 | xlabel('Vertex'); 219 | ylabel('t-value'); 220 | subplot(2,1,2); hold on; 221 | errorbar3(1:length(vertidxU),mns(vertidxU,1,subjix)',ses(vertidxU,1,subjix)','v',[1 .5 .5]); 222 | errorbar3(1:length(vertidxU),mns(vertidxU,2,subjix)',ses(vertidxU,2,subjix)','v',[.5 .5 1]); 223 | plot(mns(vertidxU,1,subjix),'r-','LineWidth',2); 224 | plot(mns(vertidxU,2,subjix),'b-','LineWidth',2); 225 | straightline(0,'h','k-'); 226 | xlabel('Vertex'); 227 | ylabel('BOLD (%)'); 228 | 229 | % We leave it as an exercise for the reader to explore: 230 | % (1) expressing cortical distance in mm units 231 | % (2) plotting multiple subjects on a single figure 232 | % (3) summarizing results across subjects 233 | 234 | 235 | -------------------------------------------------------------------------------- /matlab/example08_simplecontrastmvpa.m: -------------------------------------------------------------------------------- 1 | %% Example 8: A simple example of MVPA on the NSD betas 2 | 3 | %% Introduction 4 | 5 | % In this script, we perform an analysis that has parallels to the analysis 6 | % demonstrated in the Example 7 script. Whereas in that script we perform a 7 | % t-test to determine brain regions that show differential activity to 8 | % faces vs. non-faces, in this script we perform so-called 9 | % 'multivariate pattern analysis' (MVPA) in which we use a statistical 10 | % classifier in an attempt to decode, given activity in a set of voxels, 11 | % whether the presented stimulus was a face or non-face. The main 12 | % differences across the two approaches is (1) the direction of the 13 | % statistical mapping learned and (2) the fact that MVPA is typically 14 | % used to analyze many voxels at the same time whereas the t-test 15 | % operates on single voxels at a time. 16 | % 17 | % The example below also demonstrates the analysis approach termed 18 | % 'searchlight'; in this approach, analysis is performed throughout 19 | % the brain, but using small groups of voxels that are extracted 20 | % at different brain locations. 21 | % 22 | % Skills/concepts: 23 | % - MVPA, searchlight 24 | % - Dealing with surface representations 25 | % - Dealing with subsampled surfaces (e.g. fsaverage5) 26 | 27 | 28 | 29 | %% General setup 30 | 31 | % define 32 | expfile = '~/nsd/nsddata/experiments/nsd/nsd_expdesign.mat'; 33 | nsess = 2; % how many of the first N NSD sessions to consider 34 | betaver = 'betas_fithrf'; % which beta version to load 35 | radius = 3; % searchlight radius on fsaverage 36 | fsalt = 'fsaverage5'; % alternative surface for determining searchlight locations 37 | hemis = {'lh' 'rh'}; % strings referring to left and right hemispheres 38 | restrictroi = 'gVTC'; % restrict searchlight analysis to this ROI 39 | restrictroiix = [1]; % index into the ROI 40 | nfold = 5; % how many folds for n-fold cross-validation? 41 | 42 | % load 43 | exp1 = load(expfile); 44 | 45 | 46 | 47 | %% Load face annotations 48 | 49 | % load automated outputs: simple face count 50 | a1 = load('~/Dropbox/nsdabudhabi/nsdextensions/NSD_Annotation_Efforts_1.0/Automated/Faces/regcount_mode1.mat'); 51 | 52 | 53 | 54 | %% Load betas prepared in fsaverage space and analyze them 55 | 56 | % loop over subjects 57 | pctcorrect = single([]); % 164k vertices x 2 hemis x 8 subjects 58 | for subjix=1:8, subjix 59 | 60 | for hh=1:length(hemis) 61 | 62 | %%% load data 63 | 64 | % load in all of the betas 65 | alldata = single([]); % 327684 vertices x 750 trials x sessions 66 | for sess=1:nsess 67 | fprintf('sess%d...',sess); 68 | file0 = sprintf('~/nsd/nsddata_betas/ppdata/subj%02d/fsaverage/%s/%s.betas_session%02d.mgh',subjix,betaver,hemis{hh},sess); 69 | data = cvnloadmgz(file0); % 327684 x 1 x 1 x 750 70 | alldata(:,:,sess) = single(permute(data,[1 4 2 3])); % use single to save on memory 71 | clear data; 72 | end 73 | 74 | % prepare face counts (750 trials x sessions, non-negative integers) 75 | imageix = exp1.subjectim(subjix,exp1.masterordering(1:750*nsess)); % 1 x trials with 73k IDs 76 | counts = a1.countval(imageix); % 1 x trials with face counts 77 | counts = reshape(counts,750,[]); % 750 trials x sessions 78 | 79 | %%% prepare for searchlight 80 | 81 | % load in ROI labelings (we will use this to restrict searchlight locations) 82 | [roimask,roidescription] = cvnroimask('fsaverage',hemis{hh},restrictroi,[],[],'collapsevals'); 83 | 84 | % load in surfaces 85 | surf = cvnreadsurface('fsaverage',hemis{hh},'sphere'); 86 | surfALT = cvnreadsurface(fsalt, hemis{hh},'sphere'); 87 | hash = surf.vertices*[1000 100 1]'; assert(length(unique(hash))==size(surf.vertices,1)); 88 | hashALT = surfALT.vertices*[1000 100 1]'; assert(length(unique(hashALT))==size(surfALT.vertices,1)); 89 | XYZ = [surf.vertices ones(size(surf.vertices,1),1)]'; % prepare coordinates as 4 x V 90 | 91 | % for which fsaverage vertices will we perform searchlight analysis? 92 | mask = ismember(roimask,restrictroiix) & ... % only fsaverage vertices within the chosen ROI 93 | ismember(hash,hashALT); % only fsaverage vertices that are also in the alternative surface 94 | mask = find(mask); % a vector of indices into fsaverage vertices 95 | maskALT = []; % a corresponding vector of indices into the vertices of the alternative surface 96 | for mm=1:length(mask) 97 | maskALT(mm) = find(ismember(hashALT,hash(mask(mm)))); 98 | end 99 | 100 | %%% perform analysis 101 | 102 | % loop over searchlight locations 103 | results = zeros(1,length(mask)); % this will contain pct correct for each searchlight location 104 | for ss=1:length(mask) 105 | statusdots(ss,length(mask)); 106 | 107 | %%% figure out vertices that live within the searchlight 108 | 109 | % get 3D coordinates 110 | coord = surf.vertices(mask(ss),:); % 1 x 3 111 | 112 | % figure out rotation matrix 113 | rotmatrix = xyzrotatetoz(coord); 114 | 115 | % rotate all vertices so that the vertex is along z+ axis 116 | XYZ0 = rotmatrix*XYZ; 117 | 118 | % find the vertices above the equator and within radius mm 119 | searchix = find((XYZ0(3,:) >= 0) & (XYZ0(1,:).^2 + XYZ0(2,:).^2 <= radius^2)); % a vector of indices into fsaverage 120 | 121 | % Note that this procedure of selecting vertices within a circle placed on the 122 | % fsaverage sphere is only approximate. The fsaverage sphere is not only a 123 | % distorted brain shape (a sphere), it is also only an approximate representation 124 | % of the topology of individual subjects. A more accurate method of defining 125 | % searchlights would be to compute physical units on the native surface of 126 | % individual subjects. 127 | 128 | %%% get the data 129 | 130 | thedata = reshape(alldata(searchix,:,:),length(searchix),[]); % vertices x trials 131 | 132 | %%% do n-fold cross-validation 133 | 134 | % obtain cross-validated predictions 135 | pred = []; % 1 x trials with the predicted outcome 136 | for ff=1:nfold 137 | 138 | % split into training and testing 139 | [testix,~,trainix] = picksubset(1:size(thedata,2),[nfold ff]); 140 | 141 | % train the classifer and obtain predictions 142 | sample = thedata(:,testix)'; % testing-trials x vertices 143 | training = thedata(:,trainix)'; % training-trials x vertices 144 | group = vflatten(counts(trainix)) > 0; % training-trials x 1 with 0/1 (1 means at least one face) 145 | testclass = classify(sample,training,group,'diagLinear'); % testing-trials x 1 with the predictions 146 | pred(testix) = testclass; 147 | 148 | % Note that this is a Naive Bayes classifier, and it is just one of many 149 | % possible statistical classifiers that one could use. Common alternatives 150 | % include SVM, LDA, logistic regression, and nearest-centroid classifier. 151 | 152 | end 153 | 154 | % calculate percent correct 155 | results(ss) = mean( pred(:) == (counts(:) > 0) ) * 100; 156 | 157 | end 158 | 159 | %%% transform searchlight results to a full fsaverage representation 160 | 161 | % embed searchlight results into a vector for the alternative surface 162 | vals = copymatrix(zeros(size(surfALT.vertices,1),1),maskALT,results); 163 | 164 | % use nearest-neighbor interpolation to map to the fsaverage surface 165 | vals = cvntransfertosubject(fsalt,'fsaverage',vals(:),hemis{hh},'nearest','orig','orig'); 166 | 167 | % record the results 168 | pctcorrect(:,hh,subjix) = vals; 169 | 170 | end 171 | 172 | end 173 | 174 | 175 | 176 | %% Generate cortical map visualizations 177 | 178 | % first, we show individual subjects 179 | Lookup = []; 180 | figureprep([0 0 1000 700],1); 181 | for subjix=1:8 182 | 183 | % use ventral inflated view of fsaverage; range 50% to 80% 184 | [rawimg,Lookup,rgbimg] = cvnlookup('fsaverage',3,vflatten(pctcorrect(:,:,subjix)), ... 185 | [50 80],jet(256),[],Lookup,0); 186 | 187 | % write image to disk 188 | imwrite(rgbimg,sprintf('facepct_subj%02d.png',subjix)); 189 | 190 | % also place into figure window 191 | subplot(2,4,subjix); hold on; 192 | imshow(rgbimg); 193 | title(sprintf('Subject %d',subjix)); 194 | 195 | end 196 | %% 197 | 198 | % then, we show the group average 199 | cvnlookup('fsaverage',3,vflatten(mean(pctcorrect,3)),[50 80],jet(256),[],Lookup); 200 | colorbar; % since cvnlookup shows RGB images, we have to explicitly set the colormap 201 | colormap(jet); 202 | caxis([50 80]); 203 | %% 204 | 205 | % Notice that the spatial patterns of results are similar to those found in Example 7. 206 | %% 207 | 208 | 209 | -------------------------------------------------------------------------------- /matlab/example09_behavioraldata.m: -------------------------------------------------------------------------------- 1 | %% Example 9: Some example analyses of the behavioral data 2 | 3 | %% Introduction 4 | 5 | % In this script, we perform a few simple analyses of the behavioral data 6 | % that were collected during the NSD experiment. These data are rich and 7 | % extensive, as they reflect up to 30,000 trials from a given subject. 8 | % 9 | % Skills/concepts: 10 | % - Various plotting techniques 11 | % - Bootstrapping 12 | 13 | 14 | 15 | %% Load data 16 | 17 | % Load behavioral .tsv file. A few hints: 18 | % 2 = session 19 | % 7 = time 20 | % 9 = iscorrect 21 | % 10 = RT 22 | % 19 = missingdata 23 | thedata = {}; 24 | for subjix=1:8 25 | file0 = sprintf('~/nsd/nsddata/ppdata/subj%02d/behav/responses.tsv',subjix); 26 | a1 = importdata(file0); 27 | thedata{subjix} = a1.data; 28 | end 29 | thedata 30 | %% 31 | 32 | 33 | 34 | %% Histogram of RTs 35 | 36 | % plot histogram of RTs 37 | bins = 0:50:4200; % use the same bins for every subject 38 | figureprep([100 100 1000 400],1); 39 | for subjix=1:8 40 | subplot(2,4,subjix); hold on; 41 | hist(thedata{subjix}(:,10),bins); 42 | straightline(0:500:4200,'v','r:'); 43 | xlabel('Reaction time (ms)'); 44 | ylabel('Frequency'); 45 | title(sprintf('Subject %d',subjix)); 46 | end 47 | %% 48 | 49 | 50 | 51 | %% RTs as a function of time 52 | 53 | % define 54 | subjix = 1; 55 | 56 | % Plot RT as a function of time 57 | figure; hold on; 58 | scatter(thedata{subjix}(:,7),thedata{subjix}(:,10),'ro'); % note that NaNs just disappear 59 | xlabel('Number of days'); 60 | ylabel('Reaction time (ms)'); 61 | %% 62 | 63 | % The figure is hard to interpret given the very large 64 | % number of dots. Thus, we will also plot a summary metric. 65 | %% 66 | 67 | % Plot the median RT in each session 68 | allsess = unique(thedata{subjix}(:,2)); % all sessions 69 | time0 = []; % 1 x N with the mean time 70 | md = []; % 1 x N with the median RT 71 | for p=1:length(allsess) 72 | ix = find(thedata{subjix}(:,2) == allsess(p)); % trials to consider 73 | ix = ix(isfinite(thedata{subjix}(ix,10))); % only consider those with valid data 74 | time0(p) = mean(thedata{subjix}(ix,7)); % mean time 75 | md(p) = median(thedata{subjix}(ix,10)); % median RT 76 | end 77 | plot(time0,md,'ko-','LineWidth',2); 78 | %% 79 | 80 | % Note that the second black data point is a little funny because it 81 | % reflects data pooled from two split sessions (frankenstein session). 82 | % Reaction times were fairly stable throughout the experiment. 83 | %% 84 | 85 | 86 | 87 | %% Calculate percent correct in each session, bootstrapping to get reliability 88 | 89 | % Here, we will use bootstrapping to estimate the reliability of 90 | % the percent correct obtained in each scan session. The use of 91 | % bootstrapping is a bit overkill, as we could alternatively just 92 | % use parametric error estimates from the binomial distribution. 93 | % But, it is nonetheless useful to demonstrate how bootstrapping 94 | % can be implemented. 95 | 96 | % do it 97 | numboot = 100; 98 | pctcorrect = NaN*zeros(8,40,numboot); % initialize with NaNs 99 | for subjix=1:8 100 | for p=1:40 101 | ix = thedata{subjix}(:,2)==p; % find trials 102 | if sum(ix ~= 0) % some subjects did not complete all 40 103 | for boot=1:numboot 104 | 105 | % extract data 106 | subjdata = thedata{subjix}(ix,:); % trials x columns 107 | 108 | % perform bootstrap sampling (see also bootstrp.m) 109 | n = size(subjdata,1); % how many trials are there? 110 | bootix = ceil(n*rand(1,n)); % generate bootstrap indices 111 | bootdata = subjdata(bootix,:); % create the sample 112 | 113 | % calculate percent correct 114 | isok = bootdata(:,19)==0; % which rows have valid data? 115 | pctcorrect(subjix,p,boot) = mean(bootdata(isok,9)==1) * 100; % NaNs are treated as false here 116 | 117 | end 118 | end 119 | end 120 | end 121 | 122 | % visualize 123 | figure; hold on; 124 | cmap0 = jet(8); 125 | h1 = []; h2 = []; 126 | for subjix=1:8 127 | pp0 = prctile(pctcorrect(subjix,:,:),[16 84],3); % 1 x 40 x 2 (68% confidence interval) 128 | md0 = median(pctcorrect(subjix,:,:),3); % 1 x 40 129 | h1(subjix) = errorbar3(1:40,md0,permute(pp0,[3 2 1]),'v',(cmap0(subjix,:)+[1 1 1])/2); 130 | h2(subjix) = plot(md0,'-','LineWidth',2,'Color',cmap0(subjix,:)); 131 | end 132 | uistack(h2,'top'); % ensure the median lines are on top 133 | xlabel('Session number'); 134 | ylabel('Percent correct'); 135 | %% 136 | 137 | 138 | -------------------------------------------------------------------------------- /matlab/example10_encodingmodel.m: -------------------------------------------------------------------------------- 1 | %% Example 10: Building encoding models 2 | 3 | %% Introduction 4 | 5 | % In this script, we go through an example of building an encoding model for one 6 | % voxel in the NSD data. The model that we use is based on image contrast (computed 7 | % as the standard deviation of luminance within small square chunks of the image), 8 | % but the principles demonstrated here generalize to other types of models. 9 | % 10 | % Skills/concepts: 11 | % - OLS regression, ridge regression 12 | % - Cross-validation 13 | % - Correlated regressors 14 | % - pRF concepts and the analyzePRF.m toolbox 15 | 16 | 17 | 18 | %% General setup 19 | 20 | % define 21 | stimfile = '~/nsd/nsddata_stimuli/stimuli/nsd/nsd_stimuli.hdf5'; 22 | expfile = '~/nsd/nsddata/experiments/nsd/nsd_expdesign.mat'; 23 | nsess = 5; % how many of the first N NSD sessions to consider 24 | betaver = 'betas_fithrf'; % which beta version to load 25 | subjix = 1; 26 | ng = 17; % number of elements in contrast grid (ng x ng) 27 | npx = 25; % number of pixels that make up one grid element (npx x npx) 28 | stimdeg = 8.4; % size of stimulus in degrees of visual angle 29 | 30 | % load 31 | exp1 = load(expfile); 32 | 33 | 34 | 35 | %% Do some stimulus pre-processing 36 | 37 | % determine vector of all of the 73k IDs that are involved in the data we will load 38 | allimix = unique(exp1.subjectim(subjix,exp1.masterordering(1:750*nsess))); 39 | 40 | % load and prepare images 41 | ims = zeros(425,425,length(allimix)); 42 | for p=1:length(allimix) 43 | statusdots(p,length(allimix)); 44 | im = h5read(stimfile,'/imgBrick',[1 1 1 allimix(p)],[3 425 425 1]); 45 | im = single(rgb2gray(permute(im,[3 2 1]))); % convert to grayscale and to single format 46 | im = (im/255).^2; % convert to [0,1] and square to match display gamma 47 | ims(:,:,p) = im; 48 | end 49 | size(ims) 50 | %% 51 | 52 | % compute a "contrast grid" from the images 53 | imagecon = zeros(ng,ng,length(allimix)); 54 | for rowix=1:ng 55 | statusdots(rowix,ng); 56 | for colix=1:ng 57 | rowii = (rowix-1)*npx + (1:npx); 58 | colii = (colix-1)*npx + (1:npx); 59 | imagecon(rowix,colix,:) = std(squish(ims(rowii,colii,:),2),[],1); % standard deviation of pixels 60 | end 61 | end 62 | size(imagecon) 63 | %% 64 | 65 | 66 | 67 | %% Do some experimental-design preparation 68 | 69 | % we want a trial-length vector that provides indices into the loaded images 70 | expandtrials = []; 71 | for p=1:750*nsess 72 | thisid = exp1.subjectim(subjix,exp1.masterordering(p)); % the 73k ID 73 | expandtrials(p) = find(ismember(allimix,thisid)); % index relative to loaded images 74 | end 75 | size(expandtrials) 76 | min(expandtrials) 77 | max(expandtrials) 78 | %% 79 | 80 | 81 | 82 | %% Define the ROI that we will load from 83 | 84 | % load the Kastner atlas 85 | roi1 = load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/roi/Kastner2015.nii.gz',subjix)); 86 | 87 | % select any voxel in V1v or V1d (in either hemisphere) 88 | mask = ismember(roi1.img,[1 2]); % V1v, V1d 89 | 90 | % compute some handy indices 91 | [d1,d2,d3,dii] = computebrickandindices(mask); 92 | 93 | 94 | 95 | %% Load NSD betas for the ROI 96 | 97 | % load data 98 | data = []; % voxels x 750 trials x sessions 99 | for p=1:nsess 100 | fprintf('sess %d...',p); 101 | file0 = sprintf('~/nsd/nsddata_betas/ppdata/subj%02d/func1pt8mm/%s/betas_session%02d.mat',subjix,betaver,p); 102 | a1 = matfile(file0); 103 | temp = double(a1.betas(d1,d2,d3,:))/300; % convert to double and then convert to percent signal change 104 | temp = squish(temp,3); % flatten voxels 105 | temp = temp(dii,:); % extract the voxels we want 106 | data(:,:,p) = temp; % record 107 | end 108 | size(data) 109 | %% 110 | 111 | 112 | 113 | %% Start with a simple univariate regression 114 | 115 | % Here, we have pre-selected a single voxel and a single stimulus 116 | % feature to demonstrate some basic points. 117 | 118 | % define 119 | voxix = 59; % this is an index relative to the voxels we have loaded 120 | stimrowix = 2; 121 | stimcolix = 7; 122 | 123 | % get the data for one voxel (concatenate all trials) 124 | onevox = vflatten(data(voxix,:,:)); % trials x 1 125 | 126 | % simple inspection 127 | figure; hold on; 128 | xx = flatten(imagecon(stimrowix,stimcolix,expandtrials)); 129 | yy = flatten(onevox); 130 | scatter(xx,yy,'ro'); 131 | xlabel('Contrast'); 132 | ylabel('BOLD response (%)'); 133 | %% 134 | 135 | % In essence, this shows the key result that drives all other 136 | % analyses: the higher the contrast, the larger the BOLD response. 137 | % However, the details of the plot are hard to interpret due 138 | % to the density of the dots. 139 | %% 140 | 141 | % make a better plot 142 | figure; hold on; 143 | xbins = 0:.01:.5; 144 | ybins = -10:.2:10; 145 | [n,x,y] = hist2d(xx,yy,xbins,ybins); 146 | imagesc(x(1,:),y(:,1),log(n)); 147 | colormap(jet); 148 | straightline(0,'h','w-'); 149 | xlabel('Contrast'); 150 | ylabel('BOLD response (%)'); 151 | r = calccorrelation(xx,yy); 152 | title(sprintf('r = %.2f',r)); 153 | axis([-0.05 0.55 -11 11]); 154 | %% 155 | 156 | % We haven't really done "regression" per se; however, Pearson's 157 | % correlation is essentially regression with one predictor 158 | % plus an offset (constant) term. 159 | % 160 | % Notice: 161 | % (1) There is a clear positive relationship between the 162 | % stimulus feature (contrast) and the activity. 163 | % (2) The relationship is actually not perfectly linear. 164 | % (3) Most images tend to sample near the low end of contrast. 165 | %% 166 | 167 | 168 | 169 | %% Think about multiple regression and stimulus correlations 170 | 171 | % The activity in the voxel might be driven by the selected 172 | % grid location (row 2, column 7), but might be also driven 173 | % by nearby grid locations. The conventional approach to address 174 | % this is to fit a multiple regression model that incorporates 175 | % regressors for every grid location. However, stimulus correlations 176 | % will make this endeavor challenging. 177 | 178 | % define the reference grid element 179 | ref = imagecon(stimrowix,stimcolix,:); 180 | 181 | % compute correlation with all grid elements 182 | featurecorr = calccorrelation(repmat(ref,[ng ng]),imagecon,3); 183 | 184 | % visualize 185 | figure; hold on; 186 | imagesc(featurecorr,[-1 1]); 187 | axis image tight; 188 | set(gca,'YDir','reverse'); 189 | colormap(cmapsign4); 190 | colorbar; 191 | %% 192 | 193 | % Notice that the contrast in location (2,7) is highly correlated 194 | % with contrast in nearby locations. In fact, small positive 195 | % correlations are found everywhere. 196 | 197 | 198 | 199 | %% Perform the multiple regression 200 | 201 | % Here, we will fit an encoding model that has 17*17 = 289 202 | % parameters. The underlying model is that the response is 203 | % given as a weighted sum of the grid contrasts. No intercept 204 | % in included in this model; thus, the model implies that 205 | % having no contrast anywhere (all grid contrasts are 0) 206 | % corresponds to a BOLD response of 0%. This is sensible 207 | % given that in the way that the NSD data are prepared, 208 | % beta weights of 0% correspond to the BOLD signal intensity 209 | % during the absence of image stimulation (gray background 210 | % during blank trials). 211 | 212 | % prepare for regression 213 | X = squish(imagecon(:,:,expandtrials),2)'; % trials x features 214 | y = onevox; % trials x 1 215 | 216 | % fit the weights using ordinary least-squares 217 | h = inv(X'*X)*X'*y; 218 | 219 | % visualize 220 | figure; hold on; 221 | h1 = imagesc(reshape(h,[ng ng])); 222 | set(h1,'XData',([1 ng] - ((1+ng)/2)) * (stimdeg/ng)); 223 | set(h1,'YData',([ng 1] - ((1+ng)/2)) * (stimdeg/ng)); 224 | axis image tight; 225 | set(gca,'YDir','normal'); 226 | caxis(max(h(:)) * [-1 1]); 227 | colormap(cmapsign4); colorbar; 228 | xlabel('x-position (deg)'); 229 | ylabel('y-position (deg)'); 230 | %% 231 | 232 | % The weights appear to be noisy. This is somewhat expected given: 233 | % (i) fMRI single-trial responses are quite noisy; (ii) the large 234 | % number of parameters being fitted (289) without any regularization; 235 | % (iii) the fact that we loaded only 5 NSD sessions (3750 trials); 236 | % and (iv) the fact that the regressors are highly correlated. 237 | %% 238 | 239 | 240 | 241 | %% Build the encoding model using ridge regression 242 | 243 | % A natural solution to the challenges noted above is to use 244 | % regularization in the parameter estimation, and a classic 245 | % regularization technique is ridge regression. Here, we will 246 | % perform ridge regression and use cross-validation to select 247 | % the amount of regularization to use. 248 | 249 | % define 250 | lambdas = 10.^(-3:4); 251 | 252 | % calculate the training/testing split 253 | [testix,~,trainix] = picksubset(1:length(y),[5 1]); 254 | 255 | % do it 256 | hs = []; % weights x lambdas 257 | testerr = []; % 1 x lambdas with the mean squared error 258 | for p=1:length(lambdas) 259 | X0 = X(trainix,:); 260 | y0 = y(trainix); 261 | hs(:,p) = inv(X0'*X0 + lambdas(p)*eye(size(X0,2)))*X0'*y0; 262 | testerr(p) = mean((X(testix,:)*hs(:,p) - y(testix)).^2); % mean squared error 263 | end 264 | 265 | % which lambda was best? 266 | [~,ii] = min(testerr); 267 | hfinal = hs(:,ii); 268 | 269 | % visualize the results 270 | figureprep([100 100 1000 400],1); hold on; 271 | for p=1:length(lambdas) 272 | subplot(2,ceil(length(lambdas)/2),p); hold on; 273 | h1 = imagesc(reshape(hs(:,p),[ng ng])); 274 | set(h1,'XData',([1 ng] - ((1+ng)/2)) * (stimdeg/ng)); 275 | set(h1,'YData',([ng 1] - ((1+ng)/2)) * (stimdeg/ng)); 276 | axis image tight; 277 | set(gca,'YDir','normal'); 278 | caxis(max(hs(:,p)) * [-1 1]); 279 | colormap(cmapsign4); colorbar; 280 | xlabel('x-position (deg)'); 281 | ylabel('y-position (deg)'); 282 | title(sprintf('Lambda number %d',p)); 283 | end 284 | %% 285 | 286 | % visualize test error 287 | figure; hold on; 288 | plot(testerr,'ro-'); 289 | straightline(ii,'v','k-'); 290 | xlabel('Lambda number'); 291 | ylabel('Mean squared error'); 292 | %% 293 | 294 | % Lambda number 1 produces essentially no regularization, whereas 295 | % Lambda number 8 produces extremely heavy regularization. The 296 | % best cross-validation performance is achieved using Lambda number 5. 297 | %% 298 | 299 | 300 | 301 | %% Use analyzePRF to build the encoding model 302 | 303 | % The toolbox analyzePRF implements a 2D Gaussian pRF model. Here, we will 304 | % use it to analyze the NSD betas. While it is interesting that the tool 305 | % can be used more or less as-is on the NSD betas, there are many 306 | % conceptual differences between the two approaches, including the following: 307 | % 308 | % (1) Typically, pRF analyses are performed on binary stimulus representations that are 309 | % either OFF (0) or ON (1). In contrast, the preparation of the NSD stimuli that we 310 | % have constructed is continuous (and involves certain assumptions about the "chunk size"). 311 | % 312 | % (2) pRF analyses are typically performed on time-series data, whereas the betas we 313 | % have prepared are amplitudes (expressed in percent signal change). 314 | % 315 | % (3) The pRF model implemented in analyzePRF not only imposes a shape constraint 316 | % (2D isotropic Gaussian) on weights across the visual field, but also incorporates 317 | % a static output nonlinearity. In contrast, the regression model demonstrated in 318 | % this script is just a linear regression model with no constraints on the weights 319 | % (aside from the regularization imposed by ridge regression). 320 | % 321 | % More information on analyzePRF can be found at analyzePRF/example*.m. 322 | 323 | % run analyzePRF.m 324 | results = analyzePRF(double(imagecon(:,:,expandtrials)), ... 325 | onevox',1,struct('hrf',1,'maxpolydeg',0,'seedmode',2,'typicalgain',1)); 326 | %% 327 | 328 | 329 | 330 | %% Inspect all of the results 331 | 332 | % We are now ready to do some comparisons. We have three different sets of results. 333 | % One result is the simple linear regression model fitted to the NSD betas 334 | % using ridge regression. Another result is the 2D Gaussian pRF model (with output 335 | % nonlinearity) fitted to the NSD betas. A third result is the actual prf experiment 336 | % that was conducted on the NSD subjects and analyzed using analyzePRF.m. Note that 337 | % this reflects a completely different set of data (compared to the NSD betas). 338 | 339 | % load the prf results 340 | prfang = getfield(load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/prf_angle.nii.gz',subjix)),'img'); 341 | prfecc = getfield(load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/prf_eccentricity.nii.gz',subjix)),'img'); 342 | prfsize = getfield(load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/prf_size.nii.gz',subjix)),'img'); 343 | prfexpt = getfield(load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/prf_exponent.nii.gz',subjix)),'img'); 344 | 345 | % perform some indexing voodoo to calculate an index into the 1.8-mm volume space 346 | temp = reshape(1:numel(roi1.img),size(roi1.img)); 347 | temp = temp(d1,d2,d3); % pull out the brick 348 | temp = temp(dii); % pull out the voxels extracted from this brick 349 | volix = temp(voxix); % pull out the single voxel used in this script 350 | 351 | % make a visualization 352 | % first, deal with ridge-regression on NSD betas 353 | figureprep([100 100 600 600],1); hold on; 354 | h1 = imagesc(reshape(hfinal,[ng ng])); 355 | set(h1,'XData',([1 ng] - ((1+ng)/2)) * (stimdeg/ng)); 356 | set(h1,'YData',([ng 1] - ((1+ng)/2)) * (stimdeg/ng)); 357 | axis image tight; 358 | set(gca,'YDir','normal'); 359 | caxis(max(hfinal) * [-1 1]); 360 | colormap(cmapsign4); colorbar; 361 | xlabel('x-position (deg)'); 362 | ylabel('y-position (deg)'); 363 | % then, deal with analyzePRF on NSD betas 364 | xpos = cos(results.ang/180*pi) * results.ecc * (stimdeg/ng); 365 | ypos = sin(results.ang/180*pi) * results.ecc * (stimdeg/ng); 366 | rfsize = results.rfsize * (stimdeg/ng); 367 | set(drawellipse(xpos,ypos,0,rfsize,rfsize,[],[],'g-'),'LineWidth',3); 368 | % finally, deal with results obtained from the prf experiment 369 | xpos = cos(prfang(volix)/180*pi) * prfecc(volix); 370 | ypos = sin(prfang(volix)/180*pi) * prfecc(volix); 371 | set(drawellipse(xpos,ypos,0,prfsize(volix),prfsize(volix),[],[],'c-'),'LineWidth',3); 372 | % finish up 373 | axis([-6 6 -6 6]); 374 | %% 375 | 376 | % The three sets of results are reasonably consistent. 377 | %% 378 | 379 | 380 | 381 | %% Postmortem 382 | 383 | % There are many choices made in this example script, all of which will 384 | % have an effect on modeling results and interpretation. These include 385 | % choices with respect to data preparation: 386 | % 387 | % - We used the NSD betas in their prepared raw units of percent signal change. 388 | % Performing session-level normalization, such as z-scoring, may get rid of 389 | % unwanted instabilities across sessions and may improve results. 390 | % 391 | % And include choices with respect to the modeling setup: 392 | % 393 | % - We prepared the NSD betas at the trial level as opposed to the image level. 394 | % Thus, the cross-validation procedure does not necessarily test 395 | % generalization to new images. 396 | % 397 | % And include choices with respect to the model itself: 398 | % 399 | % - The specific "chunk size" for the contrast grid calculation was somewhat 400 | % arbitrary; changing the chunk size will change the nature of the model 401 | % and will affect the results. 402 | % - To compute contrast, we used standard deviation of luminance values. Other 403 | % ways to compute contrast are possible, such as standard deviation divided by 404 | % mean luminance. Furthermore, instead of contrast, one could use specific image 405 | % features such as the outputs of local oriented filters (e.g. Gabor functions) 406 | % applied to the stimulus. 407 | %% 408 | 409 | 410 | -------------------------------------------------------------------------------- /matlab/example11_rsa.m: -------------------------------------------------------------------------------- 1 | %% Example 11: Representational similarity analysis 2 | 3 | %% Introduction 4 | 5 | % In this script, we provide a simple example of performing 6 | % representational similarity analysis. We actually do this 7 | % not on the core NSD data but on the category-localizer floc 8 | % experiment that was also conducted in the NSD subjects. 9 | % 10 | % Skills/concepts: 11 | % - Creating representational dissimilarity matrices 12 | 13 | 14 | 15 | %% Load data 16 | 17 | % load the names of the categories used in the floc experiment 18 | catlabels = importdata('~/nsd/nsddata/experiments/floc/categories.tsv'); 19 | 20 | % load in floc betas from a ventral temporal cortex ROI 21 | betas = {}; 22 | for subjix=1:8 23 | 24 | % load in the visualsulc atlas 25 | roi1 = load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/roi/visualsulc.nii.gz',subjix)); 26 | 27 | % load in the floc betas (the 60 betas are ordered as 6 condition-splits * 10 categories) 28 | a1 = load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/floc_betas.nii.gz',subjix)); 29 | 30 | % extract data for voxels within the union of OTS, FG, mFus, CoS 31 | betas{subjix} = subscript(squish(a1.img,3),{find(ismember(roi1.img,[1 3 4 6])) ':'}); % voxels x betas 32 | 33 | end 34 | betas 35 | %% 36 | 37 | 38 | 39 | %% Construct RDMs (representational dissimilarity matrices) 40 | 41 | % do it 42 | cmatrix = []; % 10 x 10 x subjects, values are in [0,2] 43 | for subjix=1:8 44 | 45 | % average across condition-splits 46 | temp = squish(mean(reshape(betas{subjix},[],6,10),2),2); % voxels x 10 categories 47 | 48 | % calculate pairwise correlations of activity patterns and subtract from 1 49 | cmatrix(:,:,subjix) = 1-calcconfusionmatrix(temp,[],2); 50 | 51 | end 52 | 53 | 54 | 55 | %% Visualize results 56 | 57 | % plot 58 | figureprep([100 100 1000 500],1); 59 | for subjix=1:8 60 | subplot(2,4,subjix); hold on; 61 | imagesc(cmatrix(:,:,subjix),[0 0.5]); 62 | colormap(copper); colorbar; 63 | axis image tight; 64 | set(gca,'YDir','reverse'); 65 | set(gca,'YTick',1:10,'YTickLabel',catlabels); 66 | set(gca,'XTick',1:10,'XTickLabel',catlabels); 67 | xticklabel_rotate; 68 | title(sprintf('Subject %d',subjix)); 69 | end 70 | %% 71 | 72 | % Note that results will vary depending on the units of the data and the 73 | % choice of similarity metric. In the above example, we used the 74 | % beta weights as given in units of percent BOLD signal change and 75 | % used one minus Pearson's correlation as the measure of dissimilarity. 76 | % One might consider other similarity metrics and/or normalization of the 77 | % data prior to RDM construction (e.g. mean-subtraction or z-scoring 78 | % of voxel responses or activity patterns). Caution should be exercised 79 | % to ensure proper intepretation of results. 80 | 81 | 82 | -------------------------------------------------------------------------------- /matlab/example12_rsfc.m: -------------------------------------------------------------------------------- 1 | %% Example 12: Resting-state functional connectivity 2 | 3 | %% Introduction 4 | 5 | % In this script, we show an example of how one might perform a functional- 6 | % connectivity analysis on the resting-state data collected as part of NSD. 7 | % This type of data and analysis is often termed 'resting-state functional 8 | % connectivity' (RSFC). In its simplest form, RSFC is essentially just 9 | % correlating time-series across different voxels (or regions). 10 | % 11 | % Skills/concepts: 12 | % - Loading the NSD pre-processed time-series data 13 | % - Simple forms of signal processing and normalization 14 | % - Using the HCP_MMP atlas 15 | 16 | 17 | 18 | %% General setup 19 | 20 | % define 21 | subjix = 6; 22 | 23 | 24 | 25 | %% Load atlas parcellation 26 | 27 | roi1 = load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/roi/HCP_MMP1.nii.gz',subjix)); 28 | roilabel1 = read_ctab(sprintf('~/nsd/nsddata/freesurfer/subj%02d/label/HCP_MMP1.mgz.ctab',subjix)); 29 | 30 | 31 | 32 | %% Load and prepare time-series data 33 | 34 | % prepare projection matrix to perform high-pass filtering 35 | polymatrix = constructpolynomialmatrix(226,0:1); % constant term, linear term 36 | pmatrix = projectionmatrix(polymatrix); % create projection matrix 37 | 38 | % load time-series data 39 | ptimeseries = []; % time x 2 runs x 10 sessions x 180 ROIs 40 | sesstoload = 21:30; 41 | runstoload = [1 14]; 42 | for p=1:length(sesstoload) 43 | fprintf('sess=%d...',sesstoload(p)); 44 | 45 | for r=1:length(runstoload) 46 | 47 | % load data 48 | file0 = '~/nsd/nsddata_timeseries/ppdata/subj%02d/func1pt8mm/timeseries/timeseries_session%02d_run%02d.nii.gz'; 49 | a1 = load_untouch_nii(sprintf(file0,subjix,sesstoload(p),runstoload(r))); 50 | data = squish(single(a1.img),3); % XYZ x time 51 | 52 | % prepare time series for each of the ROIs in the atlas 53 | for roi=1:180 54 | 55 | % average across voxels 56 | temp = mean(data(find(roi1.img==roi),:),1)'; % time x 1 57 | 58 | % high-pass filter the data (i.e. subtract mean and linearly detrend) 59 | temp = pmatrix*temp; 60 | 61 | % z-score 62 | temp = calczscore(temp); 63 | 64 | % record 65 | ptimeseries(:,r,p,roi) = temp; 66 | 67 | % Note that RSFC analyses typically involve more aggressive procedures that 68 | % attempt to remove noise from the time-series data. Also note that in the 69 | % code above, the time series from different voxels are averaged before 70 | % detrending and normalization, but one might wish to average as the last 71 | % step instead (the order of operations will affect the result). 72 | 73 | end 74 | 75 | end 76 | end 77 | 78 | 79 | 80 | %% Visualize the data for sanity checking 81 | 82 | % plot time series for every 10th region 83 | figureprep([100 100 1000 600],1); 84 | for roi=10:10:180 85 | subplot(3,6,roi/10); hold on; 86 | plot(squish(ptimeseries(:,:,:,roi),3)); 87 | title(roilabel1.struct_names{roi}); 88 | end 89 | %% 90 | 91 | 92 | 93 | %% Perform functional connectivity 94 | 95 | % define 96 | wh = {1:10 1:2:10 2:2:10}; % define sets of sessions to analyze 97 | whstr = {'All' 'Odd' 'Even'}; % corresponding text labels 98 | 99 | % compute correlation matrices 100 | cmatrix = []; % roi x roi 101 | for p=1:length(wh) 102 | 103 | % concatenate data across runs and sessions 104 | data0 = squish(ptimeseries(:,:,wh{p},:),3); % time x roi 105 | 106 | % compute pairwise correlations 107 | cmatrix(:,:,p) = calcconfusionmatrix(data0,[],2); 108 | 109 | end 110 | 111 | % visualize the results 112 | figureprep([100 100 1000 400],1); 113 | for p=1:3 114 | subplot(1,3,p); hold on; 115 | imagesc(cmatrix(:,:,p),[0.3 1]); 116 | axis image tight; 117 | set(gca,'YDir','reverse'); 118 | colormap(copper); colorbar; 119 | title(whstr{p}); 120 | end 121 | %% 122 | 123 | % Notice that pairwise correlations are generally highly positive. 124 | % This is likely due to global noise sources in the data that tend 125 | % to cause time series to be correlated, hence motivating efforts 126 | % in the field to attempt to remove these sources of noise. 127 | %% 128 | 129 | 130 | -------------------------------------------------------------------------------- /matlab/html/example01_exploredata.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Example 1: Basic exploration of the NSD data files

Example 1: Basic exploration of the NSD data files

Contents

Introduction

% In this script, we are going to do some initial exploration of the
 70 | % types of data files that are made available as part of the prepared
 71 | % NSD data. For this, we are going to use ITK-SNAP (as a volume viewer)
 72 | % and freeview (as a surface viewer).
 73 | %
 74 | % Skills/concepts:
 75 | % - How to use ITK-SNAP and freeview
 76 | % - The nature of volumes and surfaces
 77 | % - The different types of FreeSurfer surfaces
 78 | % - MNI and fsaverage spaces
 79 | 

Use ITK-SNAP to explore volume-format data

% Load the following file into ITK-SNAP:
 80 | %   ~/nsd/nsddata/ppdata/subj01/anat/T1_0pt8_masked.nii.gz
 81 | 
% Play around in ITK-SNAP and know how to:
 82 | % - Navigate / Zoom / Pan
 83 | % - Change the colormap and color range
 84 | % - View the NIFTI header information (e.g. resolution, origin, orientation)
 85 | 
% Load as a second image the following file:
 86 | %   ~/nsd/nsddata/ppdata/subj01/anat/roi/visualsulc.nii.gz
 87 | 
% Play around and know how to:
 88 | % - Flip through different volumes ([ or ])
 89 | % - Overlay volumes using a thresholding approach
 90 | % - Overlay volumes using transparency
 91 | % - Toggle overlays on/off (w)
 92 | 
% Draw an ROI and save the ROI out as a NIFTI file called:
 93 | %   testroi.nii.gz
 94 | % Then load that file back into ITK-SNAP.
 95 | 
% Start over. Load T1_0pt8_masked.nii.gz as the main image,
 96 | % and load roi/visualsulc.nii.gz as the segmentation image.
 97 | 
% Using Label Editor, hide all of the labels and show only
 98 | % Label 3 (fusiform gyrus). Play around with the 3D volume rendering.
 99 | % Information on the meaning of the values in visualsulc is in:
100 | %   ~/nsd/nsddata/freesurfer/fsaverage/label/visualsulc.mgz.ctab
101 | 
% Quit ITK-SNAP and try launching it from the command-line:
102 | %   cd ~/nsd/nsddata/ppdata/subj01/anat/
103 | %   itksnap T1_0pt8_masked.nii.gz
104 | 
% There are many other volume viewers out there.
105 | % If you are curious, you can try, for example, mricro.
106 | 

Inspect NSD anatomical data (anat)

% The prepared NSD data come in 3 anatomical spaces (0.5 mm, 0.8 mm, and 1.0 mm).
107 | % These different spaces are exactly coincident and share exactly the same
108 | % field-of-view and origin. (The origin is placed at the center of each volume.)
109 | 
110 | % Load the 0.5-mm and 1.0-mm T1 volumes as a main image and an additional image,
111 | % respectively. Compare the two volumes.
112 | 
% Load the 0.5-mm and 1.0-mm T1 volumes in two separate ITK-SNAP sessions with
113 | % synchronized views (see Preferences). Play around a little.
114 | 
% Assess the co-registration quality between the 0.8-mm T1 and 0.8-mm T2.
115 | % Notice that compared to the T2, the T1 has a little bit of dropout in
116 | % the ventral part of the frontal lobe.
117 | 
% Inspect the Kastner atlas (roi/Kastner2015.nii.gz) overlaid on the 0.8-mm T1.
118 | % Information on the meaning of the values is in:
119 | %   ~/nsd/nsddata/freesurfer/fsaverage/label/Kastner2015.mgz.ctab
120 | 
% Explore and visualize some additional volumes:
121 | % - aseg (anatomical segmentation from FreeSurfer)
122 | % - brainmask (liberal brain mask used to de-identify data and reduce file sizes)
123 | % - EPI_to_anat1pt0 (mean EPI volume that has been warped to the anat1pt0 space)
124 | 
% Thus far, we have been visualizing data from a single subject.
125 | % To facilitate comparison across subjects, we can put subjects in a common space.
126 | % A typical one is MNI space, and the prepared NSD data already include some
127 | % MNI transformations for your convenience.
128 | 
129 | % To determine the transformation between an individual subject and MNI space,
130 | % the T1 from that subject has been nonlinearly warped to an MNI template.
131 | % Furthermore, a version of each subject's T1 that reflects this warping (and
132 | % resampling) has been saved. Let's use ITK-SNAP to quickly look at the MNI
133 | % template and each individual subject's warped T1 volume.
134 | %   cd ~/nsd/nsddata/
135 | %   itksnap -g templates/MNI152_T1_1mm.nii.gz -o ppdata/subj*/anat/T1_to_MNI.nii.gz
136 | % How good is the alignment?
137 | 

Inspect NSD functional data (func1pt8mm)

% The prepared NSD data come in 2 functional spaces (1.8 mm, 1.0 mm).
138 | % These spaces are aligned to each other, but have slightly different field-of-views
139 | % and origins (see 'nsddata description' for details). Note that the functional spaces
140 | % are not the same as the anatomical spaces.
141 | 
142 | % Load floc_facestval.nii.gz (t-value for the contrast of faces vs. non-faces from
143 | % the floc category localizer experiment) and overlay it on mean.nii.gz.
144 | 
% Alternatively, overlay the t-values on T1_to_func1pt8mm.nii.gz (this is a
145 | % version of the T1 that has already been warped to the func1pt8mm space).
146 | 
% Explore and visualize some additional volumes:
147 | % - R2 (a measure of signal quality from the NSD experiment)
148 | % - prf_eccentricity (estimate of pRF eccentricity from the prf experiment)
149 | % - valid (fraction of NSD scan sessions in which valid data were recorded)
150 | % - R2_session??.nii.gz (R2 from individual NSD scan sessions)
151 | %   Hint: itksnap -g mean.nii.gz -o R2_*.nii.gz
152 | 

Use freeview to explore surface-format data

% Compared to volume data, surface data are a bit trickier to
153 | % visualize and manage. We will use FreeSurfer's freeview as
154 | % a simple surface viewer, though there are many alternatives.
155 | 
156 | % Load subj01's left hemisphere inflated surface:
157 | %   ~/nsd/nsddata/freesurfer/subj01/surf/lh.inflated
158 | 
% Play around in freeview and know how to:
159 | % - Navigate / Rotate / Zoom / Pan
160 | 
% Load as an overlay (generic):
161 | %   ~/nsd/nsddata/freesurfer/subj01/label/lh.flocfacestval.mgz
162 | 
% Play around and know how to:
163 | % - Change the colormap and color range
164 | 
% Set the Render to 'Surface & mesh' to see the faces and vertices
165 | % that comprise a surface.
166 | 
% Set the Render back to Surface and load in:
167 | %   ~/nsd/nsddata/freesurfer/subj01/label/lh.visualsulc.mgz
168 | % Develop a good visualization of this atlas.
169 | % Save a screenshot.
170 | 
% Now visualize the same data (visualsulc) on some
171 | % other versions of the cortical surface:
172 | %   lh.white, lh.pial, lh.sphere
173 | % Use the same color settings as in the lh.inflated case and
174 | % save some screenshots to facilitate comparison.
175 | 
% Finally, to illustrate the folding-based registration
176 | % that underlies FreeSurfer's fsaverage surface, take
177 | % screenshots of the following (be careful to keep
178 | % the camera view fixed):
179 | %   (1) subj01's lh.sphere     - curvature
180 | %   (2) subj01's lh.sphere     - Kastner2015
181 | %   (3) subj01's lh.sphere.reg - curvature
182 | %   (4) subj01's lh.sphere.reg - Kastner2015
183 | %   (5) fsaverage's lh.sphere  - curvature
184 | %   (6) fsaverage's lh.sphere  - Kastner2015
185 | % Compare these screenshots and notice how sphere.reg is a
186 | % surface such that visualizing subj01's curvature on that surface
187 | % yields a map that looks well matched to fsaverage's curvature.
188 | 
% Like MNI, fsaverage is a common space that can be used to
189 | % compare results across subjects. MNI is a volume-based space
190 | % that is semi-accurate for cortex and is most accurate for
191 | % subcortical structures. In contrast, fsaverage is a surface-based
192 | % space that is applicable only to cortex.
193 | 
-------------------------------------------------------------------------------- /matlab/html/example02_loaddata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example02_loaddata.png -------------------------------------------------------------------------------- /matlab/html/example02_loaddata_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example02_loaddata_01.png -------------------------------------------------------------------------------- /matlab/html/example02_loaddata_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example02_loaddata_02.png -------------------------------------------------------------------------------- /matlab/html/example02_loaddata_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example02_loaddata_03.png -------------------------------------------------------------------------------- /matlab/html/example02_loaddata_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example02_loaddata_04.png -------------------------------------------------------------------------------- /matlab/html/example02_loaddata_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example02_loaddata_05.png -------------------------------------------------------------------------------- /matlab/html/example02_loaddata_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example02_loaddata_06.png -------------------------------------------------------------------------------- /matlab/html/example02_loaddata_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example02_loaddata_07.png -------------------------------------------------------------------------------- /matlab/html/example02_loaddata_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example02_loaddata_08.png -------------------------------------------------------------------------------- /matlab/html/example03_understandspaces.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Example 3: Understand mapping between spaces

Example 3: Understand mapping between spaces

Contents

Introduction

% nsd_mapdata.m is a utility function provided with NSD
70 | % that performs mapping between various NSD spaces (e.g. functional,
71 | % anatomical, surface). Please see nsdcode/matlab/examples_nsdmapdata.m
72 | % for a comprehensive example of how to use that function.
73 | %
74 | % Skills/concepts:
75 | % - How to use nsd_mapdata.m
76 | % - Interpolation/resampling
77 | % - Mapping between volume and surface
78 | % - Mapping between individual subjects and fsaverage
79 | 
-------------------------------------------------------------------------------- /matlab/html/example04_surfacevisualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example04_surfacevisualization.png -------------------------------------------------------------------------------- /matlab/html/example04_surfacevisualization_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example04_surfacevisualization_01.png -------------------------------------------------------------------------------- /matlab/html/example04_surfacevisualization_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example04_surfacevisualization_02.png -------------------------------------------------------------------------------- /matlab/html/example04_surfacevisualization_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example04_surfacevisualization_03.png -------------------------------------------------------------------------------- /matlab/html/example04_surfacevisualization_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example04_surfacevisualization_04.png -------------------------------------------------------------------------------- /matlab/html/example04_surfacevisualization_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example04_surfacevisualization_05.png -------------------------------------------------------------------------------- /matlab/html/example04_surfacevisualization_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example04_surfacevisualization_06.png -------------------------------------------------------------------------------- /matlab/html/example04_surfacevisualization_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example04_surfacevisualization_07.png -------------------------------------------------------------------------------- /matlab/html/example04_surfacevisualization_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example04_surfacevisualization_08.png -------------------------------------------------------------------------------- /matlab/html/example05_inspectdataatscale.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Example 5: Inspect data at scale

Example 5: Inspect data at scale

Contents

Introduction

% In this script, we demonstrate two different ways to quickly and effectively
 70 | % inspect large amounts of data. The first way is to use a high framerate
 71 | % movie, thus exploiting the rapidity of our visual system. The second way is
 72 | % to use an image to visualize many time series at once.
 73 | %
 74 | % Skills/concepts:
 75 | % - Manipulation of images, volumes, and movies
 76 | % - Image normalization and color ranges
 77 | % - aseg (from FreeSurfer)
 78 | 

Make a movie of brain volumes over time

% In this example, we will inspect all of the pre-processed fMRI time-series data
 79 | % from the first NSD session from subj01. Knowing how to use low-level routines
 80 | % to create the movie is useful as it allows you to customize the movie to your needs.
 81 | 
 82 | % define
 83 | files = '~/nsd/nsddata_timeseries/ppdata/subj01/func1pt8mm/timeseries/timeseries_session01_*.nii.gz';
 84 | outfile = 'testmovie';  % we will add file extensions later
 85 | fps = 72;               % frames per second for the movie
 86 | 
 87 | % find the files
 88 | files0 = matchfiles(files);
 89 | 
 90 | % load the data from each run and construct orthographic image inspections
 91 | im = uint8([]);
 92 | cnt = 1;
 93 | for p=1:length(files0)
 94 | 
 95 |   % load data and convert to double
 96 |   data0 = load_untouch_nii(files0{p});
 97 |   data0 = double(data0.img);
 98 | 
 99 |   % for each volume, make an image
100 |   for r=1:size(data0,4)
101 | 
102 |     % pull out middle slice in all three dimensions
103 |     imA = [];  % 120 x 120 x 3
104 |     imA = cat(3,imA,placematrix(zeros(120,120),             squeeze(data0(:,:,round(end/2),r))));
105 |     imA = cat(3,imA,placematrix(zeros(120,120),rotatematrix(squeeze(data0(:,round(end/2),:,r)),1,2,1)));
106 |     imA = cat(3,imA,placematrix(zeros(120,120),rotatematrix(squeeze(data0(round(end/2),:,:,r)),1,2,1)));
107 | 
108 |     % normalize, make a mosaic, and convert to uint8
109 |     im(:,:,cnt) = uint8(255*makeimagestack(imA,[0 2000],1,[1 3]));
110 |     cnt = cnt + 1;
111 | 
112 |     % the first ten volumes of each run get marked by a little white square
113 |     if r <= 10
114 |       im(1:4,1:4,end) = 255;
115 |     end
116 | 
117 |   end
118 | 
119 | end
120 | 
121 | % visualize one image
122 | figure; imshow(im(:,:,1));
123 | 
% write all of the images to a single .mov file
124 | imagesequencetomovie(im,sprintf('%s.mov',outfile),fps);
125 | 
126 | % make a compressed version of the movie (mpeg-4 format)
127 | unix_wrapper(sprintf('HandBrakeCLI -i %s.mov -q 2 --strict-anamorphic -o %s.m4v',outfile,outfile));
128 | 
129 | % note that instead of QTWriter (used by imagesequencetomovie.m), we could perhaps use
130 | % other utilities such as built-in MATLAB toolbox functions and/or ffmpeg.
131 | 
calling unix command:
132 | HandBrakeCLI -i testmovie.mov -q 2 --strict-anamorphic -o testmovie.m4v
133 | status of unix command:
134 | 0
135 | result of unix command:
136 | 
137 | [14:13:31] hb_init: starting libhb thread
138 | HandBrake rev5474 (2015021099) - Linux x86_64 - http://handbrake.fr
139 | 32 CPUs detected
140 | Opening testmovie.mov...
141 | [14:13:31] hb_scan: path=testmovie.mov, title_index=1
142 | libbluray/bdnav/index_parse.c:162: indx_parse(): error opening testmovie.mov/BDMV/index.bdmv
143 | libbluray/bdnav/index_parse.c:162: indx_parse(): error opening testmovie.mov/BDMV/BACKUP/index.bdmv
144 | libbluray/bluray.c:1725: nav_get_title_list(testmovie.mov) failed (0x7f79b0000900)
145 | [14:13:31] bd: not a bd - trying as a stream/file instead
146 | libdvdnav: Using dvdnav version 4.1.3
147 | libdvdread: Using libdvdcss version 1.2.10 for DVD access
148 | libdvdnav:DVDOpenFileUDF:UDFFindFile /VIDEO_TS/VIDEO_TS.IFO failed
149 | libdvdnav:DVDOpenFileUDF:UDFFindFile /VIDEO_TS/VIDEO_TS.BUP failed
150 | libdvdread: Can't open file VIDEO_TS.IFO.
151 | libdvdnav: vm: failed to read VIDEO_TS.IFO
152 | [14:13:31] dvd: not a dvd - trying as a stream/file instead
153 |  [0;39mInput #0, mov,mp4,m4a,3gp,3g2,mj2, from 'testmovie.mov':
154 |  [0m [0;39m  Metadata:
155 |  [0m [0;39m    major_brand     : qt  
156 |  [0m [0;39m    minor_version   : 537199360
157 |  [0m [0;39m    compatible_brands: qt  
158 |  [0m [0;39m    creation_time   : 2018-07-21 19:37:39
159 |  [0m [0;39m  Duration:  [0m [0;39m00:00:37.69 [0m [0;39m, start:  [0m [0;39m0.000000 [0m [0;39m, bitrate:  [0m [0;39m14295 kb/s [0m [0;39m
160 |  [0m [0;39m    Stream #0.0 [0m [0;39m(eng) [0m [0;39m: Video: png, rgb24, 363x121, 14285 kb/s [0m [0;39m, 71.94 fps [0m [0;39m, 10k tbn [0m [0;39m
161 |  [0m [0;39m    Metadata:
162 |  [0m [0;39m      creation_time   : 2018-07-21 19:37:39
163 |  [0m[14:13:31] scan: decoding previews for title 1
164 | [14:13:31] scan: 10 previews, 362x120, 71.942 fps, autocrop = 0/0/0/0, aspect 3.02:1, PAR 1:1
165 | [14:13:31] libhb: scan thread found 1 valid title(s)
166 | + title 1:
167 |   + stream: testmovie.mov
168 |   + duration: 00:00:37
169 |   + size: 362x120, pixel aspect: 1/1, display aspect: 3.02, 71.942 fps
170 |   + autocrop: 0/0/0/0
171 |   + chapters:
172 |     + 1: cells 0->0, 0 blocks, duration 00:00:37
173 |   + audio tracks:
174 |   + subtitle tracks:
175 | [14:13:31] 1 job(s) to process
176 | [14:13:31] starting job
177 | [14:13:31] sync: expecting 2712 video frames
178 | [14:13:31] job configuration:
179 | [14:13:31]  * source
180 | [14:13:31]    + testmovie.mov
181 | [14:13:31]    + title 1, chapter(s) 1 to 1
182 | [14:13:31]    + container: mov,mp4,m4a,3gp,3g2,mj2
183 | [14:13:31]    + data rate: 14295 kbps
184 | [14:13:31]  * destination
185 | [14:13:31]    + testmovie.m4v
186 | [14:13:31]    + container: MPEG-4 (.mp4 and .m4v)
187 | [14:13:31]  * video track
188 | [14:13:31]    + decoder: png
189 | [14:13:31]      + bitrate 14285 kbps
190 | [14:13:31]    + frame rate: same as source (around 71.942 fps)
191 | [14:13:31]    + filters
192 | [14:13:31]      + Framerate Shaper (0:27000000:375300)
193 | [14:13:31]        + frame rate: same as source (around 71.942 fps)
194 | [14:13:31]      + Crop and Scale (362:120:0:0:0:0)
195 | [14:13:31]        + source: 362 * 120, crop (0/0/0/0): 362 * 120, scale: 362 * 120
196 | [14:13:31]    + strict anamorphic
197 | [14:13:31]      + storage dimensions: 362 * 120, mod 0
198 | [14:13:31]      + pixel aspect ratio: 1 / 1
199 | [14:13:31]      + display dimensions: 362 * 120
200 | [14:13:31]    + encoder: MPEG-4 (FFmpeg)
201 | [14:13:31]      + quality: 2.00 (QP)
202 | [14:13:31] reader: first SCR 0 id 0x0 DTS 0
203 | [14:13:31] encavcodecInit: MPEG-4 ASP encoder
204 | [14:13:31] encavcodec: encoding at constant quantizer 236
205 | [14:13:31] encavcodec: encoding with stored aspect 1/1
206 | Encoding: task 1 of 1, 8.30 %Encoding: task 1 of 1, 18.07 %Encoding: task 1 of 1, 29.09 %Encoding: task 1 of 1, 39.97 %Encoding: task 1 of 1, 51.00 %Encoding: task 1 of 1, 62.09 %Encoding: task 1 of 1, 73.08 %Encoding: task 1 of 1, 84.11 %Encoding: task 1 of 1, 95.02 %[14:13:34] reader: done. 1 scr changes
207 | [14:13:34] work: average encoding speed for job is 0.000000 fps
208 | [14:13:34] mux: track 0, 2712 frames, 791110 bytes, 167.89 kbps, fifo 4096
209 | Muxing: this may take awhile...[14:13:34] sync: got 2712 frames, 2712 expected
210 | [14:13:34] render: lost time: 0 (0 frames)
211 | [14:13:34] render: gained time: 0 (0 frames) (0 not accounted for)
212 | [14:13:34] png-decoder done: 2712 frames, 0 decoder errors, 0 drops
213 | [14:13:34] libhb: work result = 0
214 | 
215 | Encode done!
216 | 
217 | HandBrake has exited.
218 | 
219 | 

Use a carpet plot (Power NeuroImage 2017) to quickly inspect time-series data

% In this example, we will use a carpet plot to visualize the same data as above.
220 | 
221 | % define
222 | files = '~/nsd/nsddata_timeseries/ppdata/subj01/func1pt8mm/timeseries/timeseries_session01_*.nii.gz';
223 | asegfile = '~/nsd/nsddata/ppdata/subj01/func1pt8mm/aseg.nii.gz';
224 | 
225 | % define more
226 | grayix = [42 3];  % gray matter  (rh is 42, lh is 3)
227 | wmix =   [41 2];  % white matter (rh is 41, lh is 2)
228 | csfix =  24;      % CSF
229 | maxplot = 500;    % maximum number of voxels to plot from each compartment
230 | 
231 | % use aseg to decide which voxels to extract
232 | aseg = load_untouch_nii(asegfile);
233 | grayvx = picksubset(find(ismember(aseg.img,grayix)),maxplot);
234 | wmvx   = picksubset(find(ismember(aseg.img,wmix)),maxplot);
235 | csfvx  = picksubset(find(ismember(aseg.img,csfix)),maxplot);
236 | 
237 | % calculate row bounds
238 | rowbounds = [length(grayvx) length(wmvx) length(csfvx)];
239 | rowbounds = cumsum(rowbounds);
240 | 
241 | % find the time-series files
242 | files0 = matchfiles(files);
243 | 
244 | % for each file, collect the data
245 | colbounds = [];
246 | alldata = [];
247 | for p=1:length(files0)
248 | 
249 |   % load data, convert to double, flatten voxels
250 |   data0 = load_untouch_nii(files0{p});
251 |   data0 = squish(double(data0.img),3);
252 | 
253 |   % extract data and z-score each voxel
254 |   temp = cat(1,data0(grayvx,:),data0(wmvx,:),data0(csfvx,:));
255 |   temp = calczscore(temp,2);  % there are other ways we could normalize...
256 | 
257 |   % record
258 |   alldata = cat(2,alldata,temp);
259 | 
260 |   % calculate column bounds
261 |   colbounds = [colbounds size(data0,2)];
262 | 
263 | end
264 | colbounds = cumsum(colbounds);
265 | 
266 | % make the visualization
267 | figureprep([100 100 800 600],1); hold on;
268 | imagesc(alldata,[-3 3]);
269 | colormap(gray);
270 | axis ij;
271 | axis([.5 size(alldata,2)+.5 .5 size(alldata,1)+.5]);
272 | straightline(rowbounds+.5,'h','r-');
273 | straightline(colbounds+.5,'v','r-');
274 | xlabel('Volume number');
275 | ylabel('Voxel');
276 | 
% In the plot, the three row compartments are gray matter, white matter, and CSF,
277 | % and the column compartments correspond to different fMRI runs.
278 | 
-------------------------------------------------------------------------------- /matlab/html/example05_inspectdataatscale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example05_inspectdataatscale.png -------------------------------------------------------------------------------- /matlab/html/example05_inspectdataatscale_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example05_inspectdataatscale_01.png -------------------------------------------------------------------------------- /matlab/html/example05_inspectdataatscale_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example05_inspectdataatscale_02.png -------------------------------------------------------------------------------- /matlab/html/example06_basicbetaloading.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Example 6: Basic loading and inspection of NSD betas

Example 6: Basic loading and inspection of NSD betas

Contents

Introduction

% In this script, we go through an example of loading and inspecting the NSD betas.
 70 | %
 71 | % Skills/concepts:
 72 | % - Thinking about data units
 73 | % - Data visualization
 74 | % - Simple ROI definition
 75 | % - Non-trivial indexing into the NSD data
 76 | 

General setup

% define
 77 | stimfile = '~/nsd/nsddata_stimuli/stimuli/nsd/nsd_stimuli.hdf5';
 78 | expfile = '~/nsd/nsddata/experiments/nsd/nsd_expdesign.mat';
 79 | subjix = 3;                % which NSD subject we are analyzing
 80 | nsess = 32;                % how many NSD sessions are available
 81 | betaver = 'betas_fithrf';  % which beta version to load
 82 | 

Define ROI

% We are going to examine NSD betas for one region of interest (ROI).
 83 | % Specifically, right hemisphere (RH) fusiform face area (FFA).
 84 | % Here, we go through one approach for defining that region.
 85 | 
 86 | % load in t-values for faces vs. nonfaces from the floc experiment
 87 | a1 = load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/floc_facestval.nii.gz',subjix));
 88 | 
 89 | % in ITK-SNAP, look at the NIFTI file and determine a small box in
 90 | % the appropriate anatomical location and containing high t-values (e.g. > 5)
 91 | lrix = 58:66;  % from left to right
 92 | paix = 31:54;  % from posterior to anterior
 93 | isix = 25:32;  % from inferior to superior
 94 | 
 95 | % note that this a rough-and-ready approach to defining the ROI. it is
 96 | % sufficient for the sake of this example, but a more accurate approach
 97 | % would be to examine the data on the cortical surface.
 98 | 
 99 | % create a binary volume with the box
100 | boxvol = zeros(size(a1.img));
101 | boxvol(lrix,paix,isix) = 1;
102 | 
103 | % define the ROI as within the box AND t > 5
104 | mask = boxvol & a1.img > 5;
105 | 
106 | % to aid in loading the data, compute a tight fitting box and associated indices
107 | [d1,d2,d3,ii] = computebrickandindices(mask);
108 | 

Load in the betas

% load data
109 | data = [];  % voxels x 750 trials x sessions
110 | for p=1:nsess
111 |   fprintf('sess %d...',p);
112 |   file0 = sprintf('~/nsd/nsddata_betas/ppdata/subj%02d/func1pt8mm/%s/betas_session%02d.mat',subjix,betaver,p);
113 |   a1 = matfile(file0);
114 |   temp = double(a1.betas(d1,d2,d3,:))/300;  % convert to double and then convert to percent signal change
115 |   temp = squish(temp,3);                    % flatten voxels
116 |   temp = temp(ii,:);                        % extract the voxels we want
117 |   data(:,:,p) = temp;                       % record
118 | end
119 | 
sess 1...sess 2...sess 3...sess 4...sess 5...sess 6...sess 7...sess 8...sess 9...sess 10...sess 11...sess 12...sess 13...sess 14...sess 15...sess 16...sess 17...sess 18...sess 19...sess 20...sess 21...sess 22...sess 23...sess 24...sess 25...sess 26...sess 27...sess 28...sess 29...sess 30...sess 31...sess 32...

Inspect the data

% visualize data in original units (percent signal change)
120 | figure; hold on;
121 | imagesc(reshape(data,size(data,1),[]),[-10 10]);
122 | axis ij tight;
123 | colormap(cmapsign4); colorbar;
124 | xlabel('Trial');
125 | ylabel('Voxel');
126 | title('Response (% BOLD)');
127 | 
% Notice the high heterogeneity across voxels.
128 | 
% visualize each voxel's mean PSC
129 | figure; hold on;
130 | bar(mean(mean(data,2),3));
131 | xlabel('Voxel');
132 | ylabel('Average response (% BOLD)');
133 | 
% This reinforces the point: voxels have highly different overall BOLD responses.
134 | 
% on a per-voxel basis, z-score the betas obtained in each session.
135 | % this is a fairly drastic measure, but ensures stationarity across time
136 | % and comparable units across voxels.
137 | dataZ = calczscore(data,2);
138 | 
139 | % visualize data again in z-score units
140 | figure; hold on;
141 | imagesc(reshape(dataZ,size(dataZ,1),[]),[-3 3]);
142 | axis ij tight;
143 | colormap(cmapsign4); colorbar;
144 | xlabel('Trial');
145 | ylabel('Voxel');
146 | title('Response (z-score units)');
147 | 

Load in experiment information

% load
148 | exp1 = load(expfile);
149 | theorder = exp1.masterordering(1:750*nsess);  % the trials that we have data for
150 | uniqueix = union(theorder,[]);                % unique indices into the 10k
151 | length(theorder)  % total number of trials
152 | length(uniqueix)  % total number of unique images
153 | 
154 | ans =
155 | 
156 |        24000
157 | 
158 | 
159 | ans =
160 | 
161 |         9411
162 | 
163 | 

Visualize ROI-averaged responses

% define
164 | numtodo = 100;  % number of distinct images to plot responses for
165 | 
166 | % massage dimensionality
167 | dataZ = reshape(dataZ,size(dataZ,1),[]);  % voxels x trials*sessions
168 | 
169 | % make the plot
170 | todo = picksubset(uniqueix,numtodo);  % pick a small subset to plot
171 | versions = {'Regular' 'Shuffled'};
172 | for ver=1:2
173 |   figureprep([100 100 1000 300],1); hold on;
174 |   avgresp = [];
175 |   for p=1:length(todo)
176 |     switch ver
177 |     case 1
178 |       ix = find(theorder==todo(p));              % which trials correspond to the image
179 |     case 2
180 |       ix = find(permutedim(theorder==todo(p)));  % SHUFFLE!
181 |     end
182 |     yy = mean(dataZ(:,ix),1);      % compute ROI average for each trial
183 |     scatter(repmat(p,[1 length(yy)]),yy,'ro');
184 |     avgresp(p) = mean(yy);
185 |   end
186 |   h = plot(1:length(todo),avgresp,'ko-');
187 |   set(h,'MarkerFaceColor','k');
188 |   ax = axis;
189 |   axis([ax(1:2) -2 2]);
190 |   xlabel('Image');
191 |   ylabel('Response (z-score units)');
192 |   title(versions{ver});
193 | end
194 | 
% In the 'Regular' plot, we are looking for small within-image variability
195 | % compared to the across-image variability. The 'Shuffled' plot looks slightly
196 | % different compared to the 'Regular' plot, indicating that the image-evoked
197 | % signal in the data is fairly weak. This is expected given the noise in fMRI
198 | % data, the fact that an aggressive rapid event-related design was used in NSD
199 | % (where responses to successive trials overlap substantially), and the small
200 | % number of trials per distinct image that was used in the design.
201 | 

Look at best and worst images

% compute the mean across trials only for those images with all 3 trials
202 | newdata = [];       % voxels x images with the trial-averaged response
203 | newdatastim = [];   % 1 x images with indices into the 10k
204 | for p=1:length(uniqueix)
205 |   ix = find(theorder==uniqueix(p));
206 |   if length(ix)==3
207 |     newdata(:,end+1) = mean(dataZ(:,ix),2);
208 |     newdatastim(end+1) = uniqueix(p);
209 |   end
210 | end
211 | 
212 | % sort ROI-averaged response in descending order
213 | [ss,ssix] = sort(mean(newdata,1),'descend');
214 | 
215 | % plot best and worst images
216 | strs = {'Best' 'Worst'};
217 | for flag=1:2
218 |   figureprep([100 100 1000 300],1);
219 |   for p=1:5
220 | 
221 |     % figure out 73k ID
222 |     switch flag
223 |     case 1
224 |       id73k = exp1.subjectim(subjix,newdatastim(ssix(p)));
225 |     case 2
226 |       id73k = exp1.subjectim(subjix,newdatastim(ssix(end-p+1)));
227 |     end
228 | 
229 |     % get image (425 x 425 x 3, uint8)
230 |     im = permute(h5read(stimfile,'/imgBrick',[1 1 1 id73k],[3 425 425 1]),[3 2 1]);
231 | 
232 |     % plot it
233 |     subplot(1,5,p);
234 |     imshow(im);
235 |     if p==1
236 |       title(strs{flag});
237 |     end
238 | 
239 |   end
240 | end
241 | 
% As expected, the images that most strongly drove the response tend to have faces.
242 | 
-------------------------------------------------------------------------------- /matlab/html/example06_basicbetaloading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example06_basicbetaloading.png -------------------------------------------------------------------------------- /matlab/html/example06_basicbetaloading_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example06_basicbetaloading_01.png -------------------------------------------------------------------------------- /matlab/html/example06_basicbetaloading_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example06_basicbetaloading_02.png -------------------------------------------------------------------------------- /matlab/html/example06_basicbetaloading_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example06_basicbetaloading_03.png -------------------------------------------------------------------------------- /matlab/html/example06_basicbetaloading_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example06_basicbetaloading_04.png -------------------------------------------------------------------------------- /matlab/html/example06_basicbetaloading_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example06_basicbetaloading_05.png -------------------------------------------------------------------------------- /matlab/html/example06_basicbetaloading_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example06_basicbetaloading_06.png -------------------------------------------------------------------------------- /matlab/html/example06_basicbetaloading_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example06_basicbetaloading_07.png -------------------------------------------------------------------------------- /matlab/html/example07_simplecontrast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example07_simplecontrast.png -------------------------------------------------------------------------------- /matlab/html/example07_simplecontrast_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example07_simplecontrast_01.png -------------------------------------------------------------------------------- /matlab/html/example07_simplecontrast_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example07_simplecontrast_02.png -------------------------------------------------------------------------------- /matlab/html/example07_simplecontrast_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example07_simplecontrast_03.png -------------------------------------------------------------------------------- /matlab/html/example07_simplecontrast_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example07_simplecontrast_04.png -------------------------------------------------------------------------------- /matlab/html/example07_simplecontrast_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example07_simplecontrast_05.png -------------------------------------------------------------------------------- /matlab/html/example07_simplecontrast_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example07_simplecontrast_06.png -------------------------------------------------------------------------------- /matlab/html/example07_simplecontrast_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example07_simplecontrast_07.png -------------------------------------------------------------------------------- /matlab/html/example08_simplecontrastmvpa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example08_simplecontrastmvpa.png -------------------------------------------------------------------------------- /matlab/html/example08_simplecontrastmvpa_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example08_simplecontrastmvpa_01.png -------------------------------------------------------------------------------- /matlab/html/example08_simplecontrastmvpa_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example08_simplecontrastmvpa_02.png -------------------------------------------------------------------------------- /matlab/html/example09_behavioraldata.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Example 9: Some example analyses of the behavioral data

Example 9: Some example analyses of the behavioral data

Contents

Introduction

% In this script, we perform a few simple analyses of the behavioral data
 70 | % that were collected during the NSD experiment. These data are rich and
 71 | % extensive, as they reflect up to 30,000 trials from a given subject.
 72 | %
 73 | % Skills/concepts:
 74 | % - Various plotting techniques
 75 | % - Bootstrapping
 76 | 

Load data

% Load behavioral .tsv file. A few hints:
 77 | %  2 = session
 78 | %  7 = time
 79 | %  9 = iscorrect
 80 | % 10 = RT
 81 | % 19 = missingdata
 82 | thedata = {};
 83 | for subjix=1:8
 84 |   file0 = sprintf('~/nsd/nsddata/ppdata/subj%02d/behav/responses.tsv',subjix);
 85 |   a1 = importdata(file0);
 86 |   thedata{subjix} = a1.data;
 87 | end
 88 | thedata
 89 | 
 90 | thedata =
 91 | 
 92 |   1x8 cell array
 93 | 
 94 |   Columns 1 through 3
 95 | 
 96 |     {30000x19 double}    {30000x19 double}    {24000x19 double}
 97 | 
 98 |   Columns 4 through 6
 99 | 
100 |     {22500x19 double}    {30000x19 double}    {24000x19 double}
101 | 
102 |   Columns 7 through 8
103 | 
104 |     {30000x19 double}    {22500x19 double}
105 | 
106 | 

Histogram of RTs

% plot histogram of RTs
107 | bins =  0:50:4200;  % use the same bins for every subject
108 | figureprep([100 100 1000 400],1);
109 | for subjix=1:8
110 |   subplot(2,4,subjix); hold on;
111 |   hist(thedata{subjix}(:,10),bins);
112 |   straightline(0:500:4200,'v','r:');
113 |   xlabel('Reaction time (ms)');
114 |   ylabel('Frequency');
115 |   title(sprintf('Subject %d',subjix));
116 | end
117 | 

RTs as a function of time

% define
118 | subjix = 1;
119 | 
120 | % Plot RT as a function of time
121 | figure; hold on;
122 | scatter(thedata{subjix}(:,7),thedata{subjix}(:,10),'ro');  % note that NaNs just disappear
123 | xlabel('Number of days');
124 | ylabel('Reaction time (ms)');
125 | 
% The figure is hard to interpret given the very large
126 | % number of dots. Thus, we will also plot a summary metric.
127 | 
% Plot the median RT in each session
128 | allsess = unique(thedata{subjix}(:,2));  % all sessions
129 | time0 = [];  % 1 x N with the mean time
130 | md = [];     % 1 x N with the median RT
131 | for p=1:length(allsess)
132 |   ix = find(thedata{subjix}(:,2) == allsess(p));  % trials to consider
133 |   ix = ix(isfinite(thedata{subjix}(ix,10)));      % only consider those with valid data
134 |   time0(p) = mean(thedata{subjix}(ix,7));         % mean time
135 |   md(p) = median(thedata{subjix}(ix,10));         % median RT
136 | end
137 | plot(time0,md,'ko-','LineWidth',2);
138 | 
% Note that the second black data point is a little funny because it
139 | % reflects data pooled from two split sessions (frankenstein session).
140 | % Reaction times were fairly stable throughout the experiment.
141 | 

Calculate percent correct in each session, bootstrapping to get reliability

% Here, we will use bootstrapping to estimate the reliability of
142 | % the percent correct obtained in each scan session. The use of
143 | % bootstrapping is a bit overkill, as we could alternatively just
144 | % use parametric error estimates from the binomial distribution.
145 | % But, it is nonetheless useful to demonstrate how bootstrapping
146 | % can be implemented.
147 | 
148 | % do it
149 | numboot = 100;
150 | pctcorrect = NaN*zeros(8,40,numboot);  % initialize with NaNs
151 | for subjix=1:8
152 |   for p=1:40
153 |     ix = thedata{subjix}(:,2)==p;  % find trials
154 |     if sum(ix ~= 0)                % some subjects did not complete all 40
155 |       for boot=1:numboot
156 | 
157 |         % extract data
158 |         subjdata = thedata{subjix}(ix,:);  % trials x columns
159 | 
160 |         % perform bootstrap sampling (see also bootstrp.m)
161 |         n = size(subjdata,1);              % how many trials are there?
162 |         bootix = ceil(n*rand(1,n));        % generate bootstrap indices
163 |         bootdata = subjdata(bootix,:);     % create the sample
164 | 
165 |         % calculate percent correct
166 |         isok = bootdata(:,19)==0;  % which rows have valid data?
167 |         pctcorrect(subjix,p,boot) = mean(bootdata(isok,9)==1) * 100;  % NaNs are treated as false here
168 | 
169 |       end
170 |     end
171 |   end
172 | end
173 | 
174 | % visualize
175 | figure; hold on;
176 | cmap0 = jet(8);
177 | h1 = []; h2 = [];
178 | for subjix=1:8
179 |   pp0 = prctile(pctcorrect(subjix,:,:),[16 84],3);  % 1 x 40 x 2 (68% confidence interval)
180 |   md0 = median(pctcorrect(subjix,:,:),3);           % 1 x 40
181 |   h1(subjix) = errorbar3(1:40,md0,permute(pp0,[3 2 1]),'v',(cmap0(subjix,:)+[1 1 1])/2);
182 |   h2(subjix) = plot(md0,'-','LineWidth',2,'Color',cmap0(subjix,:));
183 | end
184 | uistack(h2,'top');  % ensure the median lines are on top
185 | xlabel('Session number');
186 | ylabel('Percent correct');
187 | 
-------------------------------------------------------------------------------- /matlab/html/example09_behavioraldata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example09_behavioraldata.png -------------------------------------------------------------------------------- /matlab/html/example09_behavioraldata_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example09_behavioraldata_01.png -------------------------------------------------------------------------------- /matlab/html/example09_behavioraldata_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example09_behavioraldata_02.png -------------------------------------------------------------------------------- /matlab/html/example09_behavioraldata_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example09_behavioraldata_03.png -------------------------------------------------------------------------------- /matlab/html/example09_behavioraldata_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example09_behavioraldata_04.png -------------------------------------------------------------------------------- /matlab/html/example10_encodingmodel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example10_encodingmodel.png -------------------------------------------------------------------------------- /matlab/html/example10_encodingmodel_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example10_encodingmodel_01.png -------------------------------------------------------------------------------- /matlab/html/example10_encodingmodel_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example10_encodingmodel_02.png -------------------------------------------------------------------------------- /matlab/html/example10_encodingmodel_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example10_encodingmodel_03.png -------------------------------------------------------------------------------- /matlab/html/example10_encodingmodel_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example10_encodingmodel_04.png -------------------------------------------------------------------------------- /matlab/html/example10_encodingmodel_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example10_encodingmodel_05.png -------------------------------------------------------------------------------- /matlab/html/example10_encodingmodel_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example10_encodingmodel_06.png -------------------------------------------------------------------------------- /matlab/html/example10_encodingmodel_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example10_encodingmodel_07.png -------------------------------------------------------------------------------- /matlab/html/example11_rsa.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Example 11: Representational similarity analysis

Example 11: Representational similarity analysis

Contents

Introduction

% In this script, we provide a simple example of performing
 70 | % representational similarity analysis. We actually do this
 71 | % not on the core NSD data but on the category-localizer floc
 72 | % experiment that was also conducted in the NSD subjects.
 73 | %
 74 | % Skills/concepts:
 75 | % - Creating representational dissimilarity matrices
 76 | 

Load data

% load the names of the categories used in the floc experiment
 77 | catlabels = importdata('~/nsd/nsddata/experiments/floc/categories.tsv');
 78 | 
 79 | % load in floc betas from a ventral temporal cortex ROI
 80 | betas = {};
 81 | for subjix=1:8
 82 | 
 83 |   % load in the visualsulc atlas
 84 |   roi1 = load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/roi/visualsulc.nii.gz',subjix));
 85 | 
 86 |   % load in the floc betas (the 60 betas are ordered as 6 condition-splits * 10 categories)
 87 |   a1 = load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/floc_betas.nii.gz',subjix));
 88 | 
 89 |   % extract data for voxels within the union of OTS, FG, mFus, CoS
 90 |   betas{subjix} = subscript(squish(a1.img,3),{find(ismember(roi1.img,[1 3 4 6])) ':'});  % voxels x betas
 91 | 
 92 | end
 93 | betas
 94 | 
 95 | betas =
 96 | 
 97 |   1x8 cell array
 98 | 
 99 |   Columns 1 through 3
100 | 
101 |     {3863x60 single}    {4432x60 single}    {4253x60 single}
102 | 
103 |   Columns 4 through 6
104 | 
105 |     {3868x60 single}    {3325x60 single}    {5061x60 single}
106 | 
107 |   Columns 7 through 8
108 | 
109 |     {3289x60 single}    {4275x60 single}
110 | 
111 | 

Construct RDMs (representational dissimilarity matrices)

% do it
112 | cmatrix = [];  % 10 x 10 x subjects, values are in [0,2]
113 | for subjix=1:8
114 | 
115 |   % average across condition-splits
116 |   temp = squish(mean(reshape(betas{subjix},[],6,10),2),2);  % voxels x 10 categories
117 | 
118 |   % calculate pairwise correlations of activity patterns and subtract from 1
119 |   cmatrix(:,:,subjix) = 1-calcconfusionmatrix(temp,[],2);
120 | 
121 | end
122 | 

Visualize results

% plot
123 | figureprep([100 100 1000 500],1);
124 | for subjix=1:8
125 |   subplot(2,4,subjix); hold on;
126 |   imagesc(cmatrix(:,:,subjix),[0 0.5]);
127 |   colormap(copper); colorbar;
128 |   axis image tight;
129 |   set(gca,'YDir','reverse');
130 |   set(gca,'YTick',1:10,'YTickLabel',catlabels);
131 |   set(gca,'XTick',1:10,'XTickLabel',catlabels);
132 |   xticklabel_rotate;
133 |   title(sprintf('Subject %d',subjix));
134 | end
135 | 
% Note that results will vary depending on the units of the data and the
136 | % choice of similarity metric. In the above example, we used the
137 | % beta weights as given in units of percent BOLD signal change and
138 | % used one minus Pearson's correlation as the measure of dissimilarity.
139 | % One might consider other similarity metrics and/or normalization of the
140 | % data prior to RDM construction (e.g. mean-subtraction or z-scoring
141 | % of voxel responses or activity patterns). Caution should be exercised
142 | % to ensure proper intepretation of results.
143 | 
-------------------------------------------------------------------------------- /matlab/html/example11_rsa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example11_rsa.png -------------------------------------------------------------------------------- /matlab/html/example11_rsa_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example11_rsa_01.png -------------------------------------------------------------------------------- /matlab/html/example12_rsfc.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Example 12: Resting-state functional connectivity

Example 12: Resting-state functional connectivity

Contents

Introduction

% In this script, we show an example of how one might perform a functional-
 70 | % connectivity analysis on the resting-state data collected as part of NSD.
 71 | % This type of data and analysis is often termed 'resting-state functional
 72 | % connectivity' (RSFC). In its simplest form, RSFC is essentially just
 73 | % correlating time-series across different voxels (or regions).
 74 | %
 75 | % Skills/concepts:
 76 | % - Loading the NSD pre-processed time-series data
 77 | % - Simple forms of signal processing and normalization
 78 | % - Using the HCP_MMP atlas
 79 | 

General setup

% define
 80 | subjix = 6;
 81 | 

Load atlas parcellation

roi1 = load_untouch_nii(sprintf('~/nsd/nsddata/ppdata/subj%02d/func1pt8mm/roi/HCP_MMP1.nii.gz',subjix));
 82 | roilabel1 = read_ctab(sprintf('~/nsd/nsddata/freesurfer/subj%02d/label/HCP_MMP1.mgz.ctab',subjix));
 83 | 

Load and prepare time-series data

% prepare projection matrix to perform high-pass filtering
 84 | polymatrix = constructpolynomialmatrix(226,0:1);  % constant term, linear term
 85 | pmatrix = projectionmatrix(polymatrix);           % create projection matrix
 86 | 
 87 | % load time-series data
 88 | ptimeseries = [];  % time x 2 runs x 10 sessions x 180 ROIs
 89 | sesstoload = 21:30;
 90 | runstoload = [1 14];
 91 | for p=1:length(sesstoload)
 92 |   fprintf('sess=%d...',sesstoload(p));
 93 | 
 94 |   for r=1:length(runstoload)
 95 | 
 96 |     % load data
 97 |     file0 = '~/nsd/nsddata_timeseries/ppdata/subj%02d/func1pt8mm/timeseries/timeseries_session%02d_run%02d.nii.gz';
 98 |     a1 = load_untouch_nii(sprintf(file0,subjix,sesstoload(p),runstoload(r)));
 99 |     data = squish(single(a1.img),3);  % XYZ x time
100 | 
101 |     % prepare time series for each of the ROIs in the atlas
102 |     for roi=1:180
103 | 
104 |       % average across voxels
105 |       temp = mean(data(find(roi1.img==roi),:),1)';  % time x 1
106 | 
107 |       % high-pass filter the data (i.e. subtract mean and linearly detrend)
108 |       temp = pmatrix*temp;
109 | 
110 |       % z-score
111 |       temp = calczscore(temp);
112 | 
113 |       % record
114 |       ptimeseries(:,r,p,roi) = temp;
115 | 
116 |       % Note that RSFC analyses typically involve more aggressive procedures that
117 |       % attempt to remove noise from the time-series data. Also note that in the
118 |       % code above, the time series from different voxels are averaged before
119 |       % detrending and normalization, but one might wish to average as the last
120 |       % step instead (the order of operations will affect the result).
121 | 
122 |     end
123 | 
124 |   end
125 | end
126 | 
sess=21...sess=22...sess=23...sess=24...sess=25...sess=26...sess=27...sess=28...sess=29...sess=30...

Visualize the data for sanity checking

% plot time series for every 10th region
127 | figureprep([100 100 1000 600],1);
128 | for roi=10:10:180
129 |   subplot(3,6,roi/10); hold on;
130 |   plot(squish(ptimeseries(:,:,:,roi),3));
131 |   title(roilabel1.struct_names{roi});
132 | end
133 | 

Perform functional connectivity

% define
134 | wh = {1:10 1:2:10 2:2:10};     % define sets of sessions to analyze
135 | whstr = {'All' 'Odd' 'Even'};  % corresponding text labels
136 | 
137 | % compute correlation matrices
138 | cmatrix = [];  % roi x roi
139 | for p=1:length(wh)
140 | 
141 |   % concatenate data across runs and sessions
142 |   data0 = squish(ptimeseries(:,:,wh{p},:),3);        % time x roi
143 | 
144 |   % compute pairwise correlations
145 |   cmatrix(:,:,p) = calcconfusionmatrix(data0,[],2);
146 | 
147 | end
148 | 
149 | % visualize the results
150 | figureprep([100 100 1000 400],1);
151 | for p=1:3
152 |   subplot(1,3,p); hold on;
153 |   imagesc(cmatrix(:,:,p),[0.3 1]);
154 |   axis image tight;
155 |   set(gca,'YDir','reverse');
156 |   colormap(copper); colorbar;
157 |   title(whstr{p});
158 | end
159 | 
% Notice that pairwise correlations are generally highly positive.
160 | % This is likely due to global noise sources in the data that tend
161 | % to cause time series to be correlated, hence motivating efforts
162 | % in the field to attempt to remove these sources of noise.
163 | 
-------------------------------------------------------------------------------- /matlab/html/example12_rsfc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example12_rsfc.png -------------------------------------------------------------------------------- /matlab/html/example12_rsfc_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example12_rsfc_01.png -------------------------------------------------------------------------------- /matlab/html/example12_rsfc_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvnlab/nsdexamples/d06ba1aaa5587e05f6b043c48491c4e7a4693278/matlab/html/example12_rsfc_02.png -------------------------------------------------------------------------------- /matlab/html/index.html: -------------------------------------------------------------------------------- 1 | From http://github.com/kendrickkay/nsdexamples/ 2 | 3 |

Example 1: Basic exploration of the NSD data files 4 | 5 |

Example 2: Loading data into MATLAB 6 | 7 |

Example 3: Understand mapping between spaces 8 | 9 |

Example 4: Automated surface visualization 10 | 11 |

Example 5: Inspect data at scale 12 | 13 |

Example 6: Basic loading and inspection of NSD betas 14 | 15 |

Example 7: A simple contrast-based analysis of the NSD betas 16 | 17 |

Example 8: A simple example of MVPA on the NSD betas 18 | 19 |

Example 9: Some example analyses of the behavioral data 20 | 21 |

Example 10: Building encoding models 22 | 23 |

Example 11: Representational similarity analysis 24 | 25 |

Example 12: Resting-state functional connectivity 26 | --------------------------------------------------------------------------------