spm_parrec2nifti.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. function N = spm_parrec2nifti(parfile,opts)
  2. % Import PAR/REC images from Philips scanners into NIfTI
  3. % FORMAT N = spm_parrec2nifti(parfile,opts)
  4. % parfile - name of PAR file
  5. % opts - options structure
  6. % .ext - NIfTI file extension {'img','nii'} [default: spm_file_ext]
  7. % .outdir - output directory [default: pwd]
  8. %
  9. % N - NIfTI object
  10. %__________________________________________________________________________
  11. % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging
  12. % Guillaume Flandin
  13. % $Id: spm_parrec2nifti.m 6703 2016-01-28 17:36:10Z guillaume $
  14. %-Display warning
  15. %--------------------------------------------------------------------------
  16. fprintf('*************************************************************\n');
  17. fprintf('** PRELIMINARY SUPPORT OF PHILIPS PAR/REC **\n');
  18. fprintf('** USE AT YOUR OWN RISK **\n');
  19. fprintf('*************************************************************\n');
  20. %-Get options
  21. %--------------------------------------------------------------------------
  22. if nargin < 2, opts = struct; end
  23. if ~isfield(opts,'ext'), opts.ext = spm_file_ext; end
  24. if opts.ext(1) ~= '.', opts.ext = ['.' opts.ext]; end
  25. if ~isfield(opts,'outdir') || isempty(opts.outdir)
  26. opts.outdir = pwd;
  27. end
  28. %-Read PAR header file
  29. %--------------------------------------------------------------------------
  30. hdr = spm_par_hdr(parfile);
  31. %-Find REC data file
  32. %--------------------------------------------------------------------------
  33. if strcmp(spm_file(parfile,'ext'),'PAR')
  34. recfile = spm_file(parfile,'ext','REC');
  35. else
  36. recfile = spm_file(parfile,'ext','rec');
  37. end
  38. d = dir(recfile);
  39. if isempty(d), error('Cannot find REC file "%s".',recfile); end
  40. bytes = d.bytes;
  41. %-Write NIfTI header
  42. %--------------------------------------------------------------------------
  43. dim = [hdr.ImageInfo(1).recon_resolution hdr.MaxNumberOfSlices hdr.MaxNumberOfDynamics];
  44. dim = double(dim);
  45. dtype = double(hdr.ImageInfo(1).image_pixel_size);
  46. if prod(dim)*dtype/8 ~= bytes
  47. % can happen for corrupted files or non dynamic data
  48. dim(4) = floor(bytes*8/dtype/prod(dim(1:3)));
  49. fprintf('Size of REC file does not match PAR file. ');
  50. fprintf('Setting dim(4) to %d.\n',dim(4));
  51. end
  52. % Two scaling options [using DV here]:
  53. % DV = displayed value on console
  54. % FP = floating point value
  55. scale = hdr.ImageInfo(1).rescale_slope;
  56. inter = hdr.ImageInfo(1).rescale_intercept;
  57. mat = spm_par_orient(hdr);
  58. switch dtype
  59. case 8
  60. dtype = spm_type('int8'); % uint8?
  61. case 16
  62. dtype = spm_type('int16'); % uint16?
  63. case 32
  64. dtype = spm_type('float32');
  65. case 64
  66. dtype = spm_type('float64');
  67. otherwise
  68. error('Unknown data type.');
  69. end
  70. ofile = spm_file(parfile,'path',opts.outdir,'ext',opts.ext);
  71. dato = file_array(ofile,dim,[dtype 0],0,scale,inter);
  72. N = nifti;
  73. N.dat = dato;
  74. N.mat = mat;
  75. N.mat0 = mat;
  76. N.mat_intent = 'Scanner';
  77. N.mat0_intent = 'Scanner';
  78. N.descrip = hdr.ProtocolName;
  79. %N.timing = struct('toffset',[],'tspace',[]); % store TR
  80. create(N);
  81. %-Write NifTI data
  82. %--------------------------------------------------------------------------
  83. dati = file_array(recfile,dim,[dtype 0],0,scale,inter);
  84. dato = N.dat;
  85. % should handle interleaved data
  86. % data should not be scaled/unscaled
  87. for i=1:dim(4)
  88. slice_order = [hdr.ImageInfo([hdr.ImageInfo.dynamic_scan_number]==i).slice_number];
  89. dato(:,:,:,i) = dati(:,:,slice_order,i);
  90. end
  91. %==========================================================================
  92. % FUNCTION hdr = spm_par_hdr(fname)
  93. %==========================================================================
  94. function hdr = spm_par_hdr(fname)
  95. % could yse fileread instead
  96. fid = fopen(fname,'rt');
  97. if fid == -1
  98. error('Cannot open "%s".',fname);
  99. end
  100. iid = [];
  101. j = 1;
  102. while 1
  103. l = fgetl(fid);
  104. if ~ischar(l), break, end
  105. l = strtrim(l);
  106. if isempty(l), continue, end
  107. switch l(1)
  108. case '#'
  109. if strncmp(l,'# CLINICAL TRYOUT',17)
  110. hdr.Version = fliplr(strtok(fliplr(l)));
  111. end
  112. if strncmp(l,'# Dataset name:',15)
  113. hdr.DatasetName = strtrim(l(16:end));
  114. end
  115. if strncmp(l,'# === IMAGE INFORMATION DEFINITION',34)
  116. l = fgetl(fid); % # The rest of this file contains...
  117. l = fgetl(fid); % #
  118. iid = spm_par_get_iid(fid);
  119. end
  120. case '.'
  121. [key,val] = strtok(l(2:end),':');
  122. e = spm_par_findindict(strtrim(key));
  123. if ~isempty(e)
  124. hdr.(e.name) = feval(e.fcn,sscanf(strtrim(val(2:end)),e.fmt));
  125. else
  126. fprintf('Unknown key "%s" - ignored.\n',strtrim(key));
  127. end
  128. otherwise
  129. if isempty(iid)
  130. fclose(fid);
  131. error('Cannot read image information without definition.');
  132. end
  133. C = textscan(l,[iid.typ]);
  134. o = 1;
  135. for i=1:numel(iid)
  136. for k=1:iid(i).n
  137. hdr.ImageInfo(j).(iid(i).name)(k) = C{o};
  138. o = o + 1;
  139. end
  140. end
  141. j = j + 1;
  142. end
  143. end
  144. fclose(fid);
  145. %==========================================================================
  146. % FUNCTION iid = spm_par_get_iid(fid)
  147. %==========================================================================
  148. function iid = spm_par_get_iid(fid)
  149. % get Image Information Definition
  150. n = 1;
  151. while 1
  152. l = fgetl(fid);
  153. if ~ischar(l), break, end
  154. l = strtrim(l(2:end));
  155. if isempty(l), break, end
  156. i = strfind(l,'(');
  157. if isempty(i), continue, else i = i(end); end
  158. name = strrep(strrep(strtrim(strtok(l(1:i-1),'(<')),' ','_'),'-','_');
  159. typ = strtrim(l(i:end));
  160. [m,typ] = strtok(typ(2:end-1),'*');
  161. if isempty(typ)
  162. typ = m; m = 1;
  163. else
  164. m = str2double(m);
  165. typ = typ(2:end);
  166. end
  167. iid(n).name = name;
  168. switch typ
  169. case 'integer'
  170. iid(n).typ = strtrim(repmat('%d',1,m));
  171. case 'float'
  172. iid(n).typ = strtrim(repmat('%f',1,m));
  173. case 'string'
  174. iid(n).typ = strtrim(repmat('%s',1,m));
  175. otherwise
  176. error('Unknown data type "%s".',typ);
  177. end
  178. iid(n).n = m;
  179. n = n + 1;
  180. end
  181. %==========================================================================
  182. % FUNCTION e = spm_par_findindict(key)
  183. %==========================================================================
  184. function e = spm_par_findindict(key)
  185. dict = spm_par_dict;
  186. for i=1:numel(dict)
  187. if strcmp(dict(i).key,key)
  188. e = dict(i);
  189. return
  190. end
  191. end
  192. e = [];
  193. %==========================================================================
  194. % FUNCTION dict = spm_par_dict
  195. %==========================================================================
  196. function d = spm_par_dict
  197. persistent dict;
  198. if ~isempty(dict)
  199. d = dict;
  200. return;
  201. end
  202. n = @(x) x;
  203. dict = {...
  204. 'Patient name', 'PatientName', '%s', n
  205. 'Examination name', 'ExaminationName', '%s', n
  206. 'Protocol name', 'ProtocolName', '%s', n
  207. 'Examination date/time', 'ExaminationDate', '%s', @(x) datevec(x,'yyyy.mm.dd / HH:MM:SS')
  208. 'Series Type', 'SeriesType', '%s', n
  209. 'Series_data_type', 'SeriesDataType', '%s', n
  210. 'Acquisition nr', 'AcquisitionNr', '%d', n
  211. 'Reconstruction nr', 'ReconstructionNr', '%d', n
  212. 'Scan Duration [sec]', 'ScanDuration', '%d', n
  213. 'Max. number of cardiac phases', 'MaxNumberCardiacPhases', '%d', n
  214. 'Max. number of echoes', 'MaxNumberOfEchoes', '%d', n
  215. 'Max. number of slices/locations', 'MaxNumberOfSlices', '%d', n
  216. 'Max. number of dynamics', 'MaxNumberOfDynamics', '%d', n
  217. 'Max. number of mixes', 'MaxNumberOfMixes', '%d', n
  218. 'Patient Position', 'PatientPosition', '%s', n
  219. 'Patient position', 'PatientPosition', '%s', n
  220. 'Preparation direction', 'PreparationDirection', '%s', n
  221. 'Technique', 'Technique', '%s', n
  222. 'Scan resolution (x, y)', 'ScanResolution', '%d %d', n
  223. 'Scan mode', 'ScanMode', '%s', n
  224. 'Repetition time [ms]', 'RepetitionTime', '%f', n
  225. 'Repetition time [msec]', 'RepetitionTime', '%f', n
  226. 'FOV (ap,fh,rl) [mm]', 'FOV', '%f %f %f', n
  227. 'Water Fat shift [pixels]', 'WaterFatShift', '%f', n
  228. 'Angulation midslice(ap,fh,rl)[degr]', 'AngulationMidslice', '%f %f %f', n
  229. 'Off Centre midslice(ap,fh,rl) [mm]', 'OffCentreMidslice', '%f %f %f', n
  230. 'Flow compensation <0=no 1=yes> ?', 'FlowCompensation', '%d', @logical
  231. 'Presaturation <0=no 1=yes> ?', 'Presaturation', '%d', @logical
  232. 'Phase encoding velocity [cm/sec]', 'PhaseEncodingVelocity', '%f %f %f', n
  233. 'MTC <0=no 1=yes> ?', 'MTC', '%d', @logical
  234. 'SPIR <0=no 1=yes> ?', 'SPIR', '%d', @logical
  235. 'EPI factor <0,1=no EPI>', 'EPIFactor', '%d', n
  236. 'Dynamic scan <0=no 1=yes> ?', 'DynamicScan', '%d', @logical
  237. 'Diffusion <0=no 1=yes> ?', 'Diffusion', '%d', @logical
  238. 'Diffusion echo time [ms]', 'DiffusionEchoTime', '%f', n
  239. 'Diffusion echo time [msec]', 'DiffusionEchoTime', '%f', n
  240. 'Max. number of diffusion values', 'MaxNumberOfDiffusionValues', '%d', n
  241. 'Max. number of gradient orients', 'MaxNumberOfGradientsOrients', '%d', n
  242. 'Number of label types <0=no ASL>', 'NumberOfLabelTypes', '%d', n
  243. };
  244. dict = struct(...
  245. 'key' , dict(:,1),...
  246. 'name', dict(:,2),...
  247. 'fmt' , dict(:,3),...
  248. 'fcn' , dict(:,4));
  249. d = dict;
  250. %==========================================================================
  251. % FUNCTION M = spm_par_orient(hdr)
  252. %==========================================================================
  253. function M = spm_par_orient(hdr)
  254. dim = double([hdr.ImageInfo(1).recon_resolution hdr.MaxNumberOfSlices]);
  255. to_center = [eye(3) -dim(:)/2];
  256. to_center(4,4) = 1;
  257. vox = diag([hdr.ImageInfo(1).pixel_spacing hdr.ImageInfo(1).slice_thickness+hdr.ImageInfo(1).slice_gap 1]);
  258. switch hdr.ImageInfo(1).slice_orientation
  259. case 1 % transversal
  260. to_pls = [0 1 0 0; 0 0 1 0; 1 0 0 0; 0 0 0 1];
  261. case 2 % sagittal
  262. to_pls = diag([1 -1 -1 1]);
  263. case 3 % coronal
  264. to_pls = [0 0 1 0; 0 -1 0 0; 1 0 0 0; 0 0 0 1];
  265. otherwise
  266. error('Unknown slice orientation.');
  267. end
  268. ang = hdr.ImageInfo(1).image_angulation * pi / 180; % {radians} [AP FH RL]
  269. rz = [cos(ang(3)) -sin(ang(3)) 0; sin(ang(3)) cos(ang(3)) 0; 0 0 1];
  270. ry = [cos(ang(2)) 0 sin(ang(2)); 0 1 0; -sin(ang(2)) 0 cos(ang(2))];
  271. rx = [1 0 0; 0 cos(ang(1)) -sin(ang(1)); 0 sin(ang(1)) cos(ang(1))];
  272. rot = rx * ry *rz;
  273. rot(4,4) = 1;
  274. pls = rot * to_pls * vox * to_center;
  275. pls(1:3,4) = pls(1:3,4) + hdr.ImageInfo(1).image_offcentre(:);
  276. pls_to_ras = [0 0 -1 0; -1 0 0 0; 0 1 0 0; 0 0 0 1];
  277. M = pls_to_ras * pls;