emosex_prep_parrec2bids.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. %%% LaBGAScore_prep_parrec2bids
  2. %
  3. % This script converts Philips PARREC files to Nifti files and names and
  4. % organizes them according to BIDS specification
  5. %
  6. % USAGE
  7. %
  8. % Script should be run from the root directory of the superdataset, in this
  9. % example case /data/proj_bitter-reward, and assumes that
  10. % 1. the sourcedata, BIDS, and code subdataset are already created (using datalad if you use it)
  11. % 2. the sourcedata subdataset is organized according to LaBGAS convention
  12. % - see fmri analysis workflow Google doc on LaBGAS drive
  13. %
  14. % The script is study-specific, I indicate in the code below where
  15. % study-specific changes will need to be made
  16. %
  17. % DEPENDENCIES
  18. %
  19. % 1. spm on your Matlab path
  20. % https://www.fil.ion.ucl.ac.uk/spm/
  21. % 2. Xiangruili's dicm2nii Github repo on your Matlab path
  22. % https://github.com/xiangruili/dicm2nii
  23. %
  24. %__________________________________________________________________________
  25. %
  26. % author: lukas.vanoudenhove@kuleuven.be
  27. % date: August, 2022
  28. %
  29. %__________________________________________________________________________
  30. % @(#)% LaBGAScore_prep_parrec2bids.m v1.2
  31. % last modified: 2022/12/07
  32. %
  33. %
  34. %% STUDY-SPECIFIC OPTIONS AND SETTINGS
  35. %--------------------------------------------------------------------------
  36. nr_sess = 1; % number of sessions for each subject
  37. task1name = 'emosex_movies'; % names of task(s)
  38. slice_scan_order_exam_card = 'FH'; % get this info from your exam card, other options are 'HF', and 'interleaved'
  39. fold_over_direction_exam_card = 'AP'; % other options unknown, but this is standard on MR8
  40. fat_shift_direction_exam_card = 'P'; % other option 'A';
  41. %% DETERMINE PHASEENCODINGDIRECTION FOR JSON FILES FROM INFO ABOVE
  42. %--------------------------------------------------------------------------
  43. if strcmpi(fold_over_direction_exam_card,'AP')
  44. if strcmpi(fat_shift_direction_exam_card,'P')
  45. phase_encoding_string = 'j';
  46. elseif strcmpi(fat_shift_direction_exam_card,'A')
  47. phase_encoding_string = 'j-';
  48. else
  49. error('\nWrong option "%s" specified in fat_shift_direction_exam_card variable, check your exam card and choose between "A", or "P"\n',fat_shift_direction_exam_card)
  50. end
  51. else
  52. error('\nUnknown option "%s" specified in fold_over_direction_exam_card variable, currently only "AP" is implemented, check with your MR physicist before proceeding\n',fold_over_direction_exam_card)
  53. end
  54. %% DEFINE DIRECTORIES AND ADD CODE DIR TO MATLAB PATH
  55. %--------------------------------------------------------------------------
  56. rootdir = pwd;
  57. githubrootdir = '/data/master_github_repos';
  58. sourcedir = fullfile(rootdir,'sourcedata');
  59. BIDSdir = fullfile(rootdir,'BIDS');
  60. codedir = fullfile(rootdir,'code');
  61. matlabpath = path;
  62. if ~exist('spm.m','file')
  63. spmpathcommand = "addpath('your_spm_rootdir','-end')";
  64. error('\nspm12 not found on Matlab path, please add WITHOUT subfolders using the Matlab GUI or type %s in Matlab terminal before proceeding',spmpathcommand)
  65. else
  66. spmrootdir = which('spm.m');
  67. [spmrootdir,~,~] = fileparts(spmrootdir);
  68. end
  69. if sum(contains(matlabpath,codedir)) == 0
  70. addpath(genpath(codedir),'-end');
  71. warning('\nadding %s to end of Matlab path\n',codedir)
  72. end
  73. %% READ IN SUBJECT LIST FROM SOURCEDATA AND WRITE IN BIDSDIR
  74. %--------------------------------------------------------------------------
  75. %sourcelist = dir(fullfile(sourcedir,'sub-*'));
  76. sourcelist = dir(fullfile(sourcedir,'sub-055*'));
  77. sourcesubjs = cellstr(char(sourcelist(:).name));
  78. sourcesubjdirs = cell(size(sourcesubjs,1),1);
  79. for sourcesub = 1:size(sourcesubjs,1)
  80. sourcesubjdirs{sourcesub,1} = fullfile(sourcelist(sourcesub).folder, sourcelist(sourcesub).name);
  81. end
  82. for sub = 1:size(sourcesubjdirs,1)
  83. subjBIDSdir = fullfile(BIDSdir,sourcesubjs{sub});
  84. if ~exist(subjBIDSdir,'dir')
  85. mkdir(subjBIDSdir);
  86. end
  87. if nr_sess > 1
  88. for sess = 1:nr_sess
  89. sessid = sprintf('ses-%d',sess);
  90. subjsessdir = fullfile(subjBIDSdir,sessid);
  91. if ~exist(subjsessdir,'dir')
  92. mkdir(subjsessdir);
  93. mkdir(subjsessdir,'anat');
  94. mkdir(subjsessdir,'func');
  95. end
  96. end
  97. end
  98. clear subjBIDSdir sess sessid subjsessdir
  99. end
  100. clear sub
  101. %BIDSlist = dir(fullfile(BIDSdir,'sub-*'));
  102. BIDSlist = dir(fullfile(BIDSdir,'sub-055*'));
  103. BIDSsubjs = cellstr(char(BIDSlist(:).name));
  104. BIDSsubjdirs = cell(size(BIDSsubjs,1),1);
  105. for BIDSsub = 1:size(BIDSsubjs,1)
  106. BIDSsubjdirs{BIDSsub,1} = fullfile(BIDSlist(BIDSsub).folder, BIDSlist(BIDSsub).name);
  107. end
  108. %% CONVERT PARRECS TO NIFTI AND WRITE TO THE RIGHT PLACE
  109. %--------------------------------------------------------------------------
  110. for sub = 1:size(sourcesubjdirs,1)
  111. %for sub = 1
  112. if nr_sess == 1
  113. subjsourcedir = fullfile(sourcedir,sourcesubjs{sub},'PARREC');
  114. subjBIDSdir = fullfile(BIDSdir, sourcesubjs{sub});
  115. dicm2nii(char(subjsourcedir),char(subjBIDSdir),1);
  116. pf.save_json = getpref('dicm2nii_gui_para', 'save_json', true); %this line is different then parrec2bids of bittersweet reward
  117. load(fullfile(subjBIDSdir,'dcmHeaders.mat'));
  118. if ~exist(fullfile(subjBIDSdir, 'anat'), 'dir');
  119. mkdir(subjBIDSdir, 'anat')
  120. end
  121. if ~exist(fullfile(subjBIDSdir, 'func'), 'dir');
  122. mkdir(subjBIDSdir, 'func')
  123. end
  124. parrec_headers = fields(h);
  125. anatniilist = dir(fullfile(subjBIDSdir,'*_4_*.nii.gz')); % STUDY-SPECIFIC: THE NAMES OF YOUR STRUCTURAL PARRECS MAY DIFFER
  126. for i = 1:size(anatniilist,1)
  127. sourceanatniiname = char(anatniilist(i).name);
  128. BIDSanatniiname = char(strcat(sourcesubjs{sub},'_T1w.nii.gz'));
  129. movefile(fullfile(subjBIDSdir,sourceanatniiname),fullfile(subjBIDSdir,'anat',BIDSanatniiname));
  130. end
  131. anatjsonlist = dir(fullfile(subjBIDSdir,'*_4_*.json')); % STUDY-SPECIFIC: THE NAMES OF YOUR STRUCTURAL PARRECS MAY DIFFER
  132. for i = 1:size(anatjsonlist,1)
  133. sourceanatjsonname = char(anatjsonlist(i).name);
  134. anatjson = spm_jsonread(fullfile(subjBIDSdir,sourceanatjsonname));
  135. anatjson.SeriesDescription = [];
  136. spm_jsonwrite(fullfile(subjBIDSdir,sourceanatjsonname),anatjson);
  137. BIDSanatjsonname = char(strcat(sourcesubjs{sub},'_T1w.json'));
  138. movefile(fullfile(subjBIDSdir,sourceanatjsonname),fullfile(subjBIDSdir,'anat',BIDSanatjsonname));
  139. end
  140. task1niilist1 = dir(fullfile(subjBIDSdir,'*_5_*.nii.gz')); % STUDY-SPECIFIC: THE NAMES OF YOUR FUNCTIONAL PARRECS MAY DIFFER
  141. task1niilist = [task1niilist1];
  142. for i = 1:size(task1niilist,1)
  143. runid = sprintf('_run-%d',i);
  144. sourcetask1niiname = char(task1niilist(i).name);
  145. BIDStask1niiname = char(strcat(sourcesubjs{sub},'_task-', task1name, runid, '_bold.nii.gz'));
  146. movefile(fullfile(subjBIDSdir,sourcetask1niiname),fullfile(subjBIDSdir,'func',BIDStask1niiname));
  147. end
  148. task1jsonlist1 = dir(fullfile(subjBIDSdir,'*_5_*.json')); % STUDY-SPECIFIC: THE NAMES OF YOUR FUNCTIONAL PARRECS MAY DIFFER
  149. task1jsonlist = [task1jsonlist1];
  150. for i = 1:size(task1jsonlist,1)
  151. runid = sprintf('_run-%d',i);
  152. sourcetask1jsonname = char(task1jsonlist(i).name);
  153. % the following is based on Chris Rorden's code
  154. % https://neurostars.org/t/deriving-slice-timing-order-from-philips-par-rec-and-console-information/17688
  155. n_slices = h.(parrec_headers{size(anatjsonlist,1)+i}).LocationsInAcquisition;
  156. TR = (h.(parrec_headers{size(anatjsonlist,1)+i}).RepetitionTime)/1000; % convert to seconds
  157. last_slice_time = TR - TR/n_slices;
  158. switch slice_scan_order_exam_card
  159. case 'FH' % ascending
  160. slice_times = linspace(0, last_slice_time, n_slices);
  161. case 'HF' % descending
  162. slice_times = flip(linspace(0, last_slice_time, n_slices));
  163. case 'interleaved'
  164. order = [1:2:n_slices 2:2:n_slices];
  165. slice_times = linspace(0, last_slice_time, n_slices);
  166. slice_times(order) = slice_times;
  167. otherwise
  168. error('\nWrong option "%s" specified in slice_scan_order_exam_card variable, check your exam card and choose between "FH", HF", or "interleaved"\n',slice_scan_order_exam_card)
  169. end
  170. task1json = spm_jsonread(fullfile(subjBIDSdir,sourcetask1jsonname));
  171. task1json.SeriesDescription = [];
  172. task1json.PhaseEncodingDirection = phase_encoding_string;
  173. task1json.TaskName = task1name;
  174. task1json.SliceTiming = slice_times;
  175. spm_jsonwrite(fullfile(subjBIDSdir,sourcetask1jsonname),task1json);
  176. BIDStask1jsonname = char(strcat(sourcesubjs{sub},'_task-', task1name , runid, '_bold.json'));
  177. movefile(fullfile(subjBIDSdir,sourcetask1jsonname),fullfile(subjBIDSdir,'func',BIDStask1jsonname));
  178. end
  179. else
  180. for sess = 1:nr_sess
  181. sessid = sprintf('ses-%d',sess);
  182. subjsourcesessdir = fullfile(sourcedir,sourcesubjs{sub},sessid,'PARREC');
  183. subjBIDSsessdir = fullfile(BIDSdir,sourcesubjs{sub},sessid);
  184. dicm2nii(char(subjsourcesessdir),char(subjBIDSsessdir),1);
  185. load(fullfile(subjBIDSsessdir,'dcmHeaders.mat'));
  186. parrec_headers = fields(h);
  187. anatniilist = dir(fullfile(subjBIDSsessdir,'*_3DTFE_*.nii.gz')); % STUDY-SPECIFIC: THE NAMES OF YOUR STRUCTURAL PARRECS MAY DIFFER
  188. for i = 1:size(anatniilist,1)
  189. sourceanatniiname = char(anatniilist(i).name);
  190. BIDSanatniiname = char(strcat(sourcesubjs{sub},'_',sessid,'_T1w.nii.gz'));
  191. movefile(fullfile(subjBIDSsessdir,sourceanatniiname),fullfile(subjBIDSsessdir,'anat',BIDSanatniiname));
  192. end
  193. anatjsonlist = dir(fullfile(subjBIDSsessdir,'*_3DTFE_*.json')); % STUDY-SPECIFIC: THE NAMES OF YOUR STRUCTURAL PARRECS MAY DIFFER
  194. for i = 1:size(anatjsonlist,1)
  195. sourceanatjsonname = char(anatjsonlist(i).name);
  196. anatjson = spm_jsonread(fullfile(subjBIDSsessdir,sourceanatjsonname));
  197. anatjson.SeriesDescription = [];
  198. spm_jsonwrite(fullfile(subjBIDSsessdir,sourceanatjsonname),anatjson);
  199. BIDSanatjsonname = char(strcat(sourcesubjs{sub},'_',sessid,'_T1w.json'));
  200. movefile(fullfile(subjBIDSsessdir,sourceanatjsonname),fullfile(subjBIDSsessdir,'anat',BIDSanatjsonname));
  201. end
  202. task1niilist1 = dir(fullfile(subjBIDSsessdir,'*_Exp1_*.nii.gz')); % STUDY-SPECIFIC: THE NAMES OF YOUR FUNCTIONAL PARRECS MAY DIFFER
  203. task1niilist2 = dir(fullfile(subjBIDSsessdir,'*_Exp2_*.nii.gz'));
  204. task1niilist = [task1niilist1;task1niilist2];
  205. for i = 1:size(task1niilist,1)
  206. runid = sprintf('_run-%d',i);
  207. sourcetask1niiname = char(task1niilist(i).name);
  208. BIDStask1niiname = char(strcat(sourcesubjs{sub},'_',sessid,'_task-', task1name, runid, '_bold.nii.gz'));
  209. movefile(fullfile(subjBIDSsessdir,sourcetask1niiname),fullfile(subjBIDSsessdir,'func',BIDStask1niiname));
  210. end
  211. task1jsonlist1 = dir(fullfile(subjBIDSsessdir,'*_Exp1_*.json')); % STUDY-SPECIFIC: THE NAMES OF YOUR FUNCTIONAL PARRECS MAY DIFFER
  212. task1jsonlist2 = dir(fullfile(subjBIDSsessdir,'*_Exp2_*.json'));
  213. task1jsonlist = [task1jsonlist1;task1jsonlist2];
  214. for i = 1:size(task1jsonlist,1)
  215. runid = sprintf('_run-%d',i);
  216. sourcetask1jsonname = char(task1jsonlist(i).name);
  217. % the following is based on Chris Rorden's code
  218. % https://neurostars.org/t/deriving-slice-timing-order-from-philips-par-rec-and-console-information/17688
  219. n_slices = h.(parrec_headers{size(anatjsonlist,1)+i}).LocationsInAcquisition;
  220. TR = (h.(parrec_headers{size(anatjsonlist,1)+i}).RepetitionTime)/1000; % convert to seconds
  221. last_slice_time = TR - TR/n_slices;
  222. switch slice_scan_order_exam_card
  223. case 'FH' % ascending
  224. slice_times = linspace(0, last_slice_time, n_slices);
  225. case 'HF' % descending
  226. slice_times = flip(linspace(0, last_slice_time, n_slices));
  227. case 'interleaved'
  228. order = [1:2:n_slices 2:2:n_slices];
  229. slice_times = linspace(0, last_slice_time, n_slices);
  230. slice_times(order) = slice_times;
  231. otherwise
  232. error('\nWrong option "%s" specified in slice_scan_order_exam_card variable, check your exam card and choose between "FH", HF", or "interleaved"\n',slice_scan_order_exam_card)
  233. end
  234. task1json = spm_jsonread(fullfile(subjBIDSsessdir,sourcetask1jsonname));
  235. task1json.SeriesDescription = [];
  236. task1json.PhaseEncodingDirection = phase_encoding_string;
  237. task1json.TaskName = task1name;
  238. task1json.SliceTiming = slice_times;
  239. spm_jsonwrite(fullfile(subjBIDSsessdir,sourcetask1jsonname),task1json);
  240. BIDStask1jsonname = char(strcat(sourcesubjs{sub},'_task-', task1name , runid, '_bold.json'));
  241. movefile(fullfile(subjBIDSsessdir,sourcetask1jsonname),fullfile(subjBIDSsessdir,'func',BIDStask1jsonname));
  242. end
  243. task2niilist1 = dir(fullfile(subjBIDSsessdir,'*_Exp3_*.nii.gz')); % STUDY-SPECIFIC: THE NAMES OF YOUR FUNCTIONAL PARRECS MAY DIFFER
  244. task2niilist2 = dir(fullfile(subjBIDSsessdir,'*_Exp4_*.nii.gz'));
  245. task2niilist = [task2niilist1;task2niilist2];
  246. for i = 1:size(task2niilist,1)
  247. runid = sprintf('_run-%d',i);
  248. sourcetask2niiname = char(task2niilist(i).name);
  249. BIDStask2niiname = char(strcat(sourcesubjs{sub},'_',sessid,'_task-', task2name, runid, '_bold.nii.gz'));
  250. movefile(fullfile(subjBIDSsessdir,sourcetask2niiname),fullfile(subjBIDSsessdir,'func',BIDStask2niiname));
  251. end
  252. task2jsonlist1 = dir(fullfile(subjBIDSsessdir,'*_Exp3_*.json')); % STUDY-SPECIFIC: THE NAMES OF YOUR FUNCTIONAL PARRECS MAY DIFFER
  253. task2jsonlist2 = dir(fullfile(subjBIDSsessdir,'*_Exp4_*.json'));
  254. task2jsonlist = [task2jsonlist1;task2jsonlist2];
  255. for i = 1:size(task2jsonlist,1)
  256. runid = sprintf('_run-%d',i);
  257. sourcetask2jsonname = char(task2jsonlist(i).name);
  258. % the following is based on Chris Rorden's code
  259. % https://neurostars.org/t/deriving-slice-timing-order-from-philips-par-rec-and-console-information/17688
  260. n_slices = h.(parrec_headers{size(anatjsonlist,1)+size(task1jsonlist,1)+i}).LocationsInAcquisition;
  261. TR = (h.(parrec_headers{size(anatjsonlist,1)+size(task1jsonlist,1)+i}).RepetitionTime)/1000; % convert to seconds
  262. last_slice_time = TR - TR/n_slices;
  263. switch slice_scan_order_exam_card
  264. case 'FH' % ascending
  265. slice_times = linspace(0, last_slice_time, n_slices);
  266. case 'HF' % descending
  267. slice_times = flip(linspace(0, last_slice_time, n_slices));
  268. case 'interleaved'
  269. order = [1:2:n_slices 2:2:n_slices];
  270. slice_times = linspace(0, last_slice_time, n_slices);
  271. slice_times(order) = slice_times;
  272. otherwise
  273. error('\nWrong option "%s" specified in slice_scan_order_exam_card variable, check your exam card and choose between "FH", HF", or "interleaved"\n',slice_scan_order_exam_card)
  274. end
  275. task2json = spm_jsonread(fullfile(subjBIDSsessdir,sourcetask2jsonname));
  276. task2json.SeriesDescription = [];
  277. task2json.PhaseEncodingDirection = phase_encoding_string;
  278. task2json.TaskName = task2name;
  279. task2json.SliceTiming = slice_times;
  280. spm_jsonwrite(fullfile(subjBIDSsessdir,sourcetask2jsonname),task2json);
  281. BIDStask2jsonname = char(strcat(sourcesubjs{sub},'_',sessid,'_task-', task2name, runid, '_bold.json'));
  282. movefile(fullfile(subjBIDSsessdir,sourcetask2jsonname),fullfile(subjBIDSsessdir,'func',BIDStask2jsonname));
  283. end
  284. movefile(fullfile(subjBIDSsessdir,'dcmHeaders.mat'), fullfile(subjsourcesessdir,'dcmHeaders.mat')); % move to sourcedata to prevent BIDS validator from erroring
  285. end % for loop over sessions
  286. end % if loop single versus multiple sessions
  287. end % for loop over subjects