Scheduled service maintenance on November 22


On Friday, November 22, 2024, between 06:00 CET and 18:00 CET, GIN services will undergo planned maintenance. Extended service interruptions should be expected. We will try to keep downtimes to a minimum, but recommend that users avoid critical tasks, large data uploads, or DOI requests during this time.

We apologize for any inconvenience.

xform_nii.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. % internal function
  2. % 'xform_nii.m' is an internal function called by "load_nii.m", so
  3. % you do not need run this program by yourself. It does simplified
  4. % NIfTI sform/qform affine transform, and supports some of the
  5. % affine transforms, including translation, reflection, and
  6. % orthogonal rotation (N*90 degree).
  7. %
  8. % For other affine transforms, e.g. any degree rotation, shearing
  9. % etc. you will have to use the included 'reslice_nii.m' program
  10. % to reslice the image volume. 'reslice_nii.m' is not called by
  11. % any other program, and you have to run 'reslice_nii.m' explicitly
  12. % for those NIfTI files that you want to reslice them.
  13. %
  14. % Since 'xform_nii.m' does not involve any interpolation or any
  15. % slice change, the original image volume is supposed to be
  16. % untouched, although it is translated, reflected, or even
  17. % orthogonally rotated, based on the affine matrix in the
  18. % NIfTI header.
  19. %
  20. % However, the affine matrix in the header of a lot NIfTI files
  21. % contain slightly non-orthogonal rotation. Therefore, optional
  22. % input parameter 'tolerance' is used to allow some distortion
  23. % in the loaded image for any non-orthogonal rotation or shearing
  24. % of NIfTI affine matrix. If you set 'tolerance' to 0, it means
  25. % that you do not allow any distortion. If you set 'tolerance' to
  26. % 1, it means that you do not care any distortion. The image will
  27. % fail to be loaded if it can not be tolerated. The tolerance will
  28. % be set to 0.1 (10%), if it is default or empty.
  29. %
  30. % Because 'reslice_nii.m' has to perform 3D interpolation, it can
  31. % be slow depending on image size and affine matrix in the header.
  32. %
  33. % After you perform the affine transform, the 'nii' structure
  34. % generated from 'xform_nii.m' or new NIfTI file created from
  35. % 'reslice_nii.m' will be in RAS orientation, i.e. X axis from
  36. % Left to Right, Y axis from Posterior to Anterior, and Z axis
  37. % from Inferior to Superior.
  38. %
  39. % NOTE: This function should be called immediately after load_nii.
  40. %
  41. % Usage: [ nii ] = xform_nii(nii, [tolerance], [preferredForm])
  42. %
  43. % nii - NIFTI structure (returned from load_nii)
  44. %
  45. % tolerance (optional) - distortion allowed for non-orthogonal rotation
  46. % or shearing in NIfTI affine matrix. It will be set to 0.1 (10%),
  47. % if it is default or empty.
  48. %
  49. % preferredForm (optional) - selects which transformation from voxels
  50. % to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate
  51. % "prefer sform or qform, but use others if preferred not present".
  52. % Upper case indicate the program is forced to use the specificied
  53. % tranform or fail loading. 'preferredForm' will be 's', if it is
  54. % default or empty. - Jeff Gunter
  55. %
  56. % NIFTI data format can be found on: http://nifti.nimh.nih.gov
  57. %
  58. % - Jimmy Shen (jimmy@rotman-baycrest.on.ca)
  59. %
  60. function nii = xform_nii(nii, tolerance, preferredForm)
  61. % save a copy of the header as it was loaded. This is the
  62. % header before any sform, qform manipulation is done.
  63. %
  64. nii.original.hdr = nii.hdr;
  65. if ~exist('tolerance','var') | isempty(tolerance)
  66. tolerance = 0.1;
  67. elseif(tolerance<=0)
  68. tolerance = eps;
  69. end
  70. if ~exist('preferredForm','var') | isempty(preferredForm)
  71. preferredForm= 's'; % Jeff
  72. end
  73. % if scl_slope field is nonzero, then each voxel value in the
  74. % dataset should be scaled as: y = scl_slope * x + scl_inter
  75. % I bring it here because hdr will be modified by change_hdr.
  76. %
  77. if nii.hdr.dime.scl_slope ~= 0 & ...
  78. ismember(nii.hdr.dime.datatype, [2,4,8,16,64,256,512,768]) & ...
  79. (nii.hdr.dime.scl_slope ~= 1 | nii.hdr.dime.scl_inter ~= 0)
  80. nii.img = ...
  81. nii.hdr.dime.scl_slope * double(nii.img) + nii.hdr.dime.scl_inter;
  82. if nii.hdr.dime.datatype == 64
  83. nii.hdr.dime.datatype = 64;
  84. nii.hdr.dime.bitpix = 64;
  85. else
  86. nii.img = single(nii.img);
  87. nii.hdr.dime.datatype = 16;
  88. nii.hdr.dime.bitpix = 32;
  89. end
  90. nii.hdr.dime.glmax = max(double(nii.img(:)));
  91. nii.hdr.dime.glmin = min(double(nii.img(:)));
  92. % set scale to non-use, because it is applied in xform_nii
  93. %
  94. nii.hdr.dime.scl_slope = 0;
  95. end
  96. % However, the scaling is to be ignored if datatype is DT_RGB24.
  97. % If datatype is a complex type, then the scaling is to be applied
  98. % to both the real and imaginary parts.
  99. %
  100. if nii.hdr.dime.scl_slope ~= 0 & ...
  101. ismember(nii.hdr.dime.datatype, [32,1792])
  102. nii.img = ...
  103. nii.hdr.dime.scl_slope * double(nii.img) + nii.hdr.dime.scl_inter;
  104. if nii.hdr.dime.datatype == 32
  105. nii.img = single(nii.img);
  106. end
  107. nii.hdr.dime.glmax = max(double(nii.img(:)));
  108. nii.hdr.dime.glmin = min(double(nii.img(:)));
  109. % set scale to non-use, because it is applied in xform_nii
  110. %
  111. nii.hdr.dime.scl_slope = 0;
  112. end
  113. % There is no need for this program to transform Analyze data
  114. %
  115. if nii.filetype == 0 & exist([nii.fileprefix '.mat'],'file')
  116. load([nii.fileprefix '.mat']); % old SPM affine matrix
  117. R=M(1:3,1:3);
  118. T=M(1:3,4);
  119. T=R*ones(3,1)+T;
  120. M(1:3,4)=T;
  121. nii.hdr.hist.qform_code=0;
  122. nii.hdr.hist.sform_code=1;
  123. nii.hdr.hist.srow_x=M(1,:);
  124. nii.hdr.hist.srow_y=M(2,:);
  125. nii.hdr.hist.srow_z=M(3,:);
  126. elseif nii.filetype == 0
  127. nii.hdr.hist.rot_orient = [];
  128. nii.hdr.hist.flip_orient = [];
  129. return; % no sform/qform for Analyze format
  130. end
  131. hdr = nii.hdr;
  132. [hdr,orient]=change_hdr(hdr,tolerance,preferredForm);
  133. % flip and/or rotate image data
  134. %
  135. if ~isequal(orient, [1 2 3])
  136. old_dim = hdr.dime.dim([2:4]);
  137. % More than 1 time frame
  138. %
  139. if ndims(nii.img) > 3
  140. pattern = 1:prod(old_dim);
  141. else
  142. pattern = [];
  143. end
  144. if ~isempty(pattern)
  145. pattern = reshape(pattern, old_dim);
  146. end
  147. % calculate for rotation after flip
  148. %
  149. rot_orient = mod(orient + 2, 3) + 1;
  150. % do flip:
  151. %
  152. flip_orient = orient - rot_orient;
  153. for i = 1:3
  154. if flip_orient(i)
  155. if ~isempty(pattern)
  156. pattern = flipdim(pattern, i);
  157. else
  158. nii.img = flipdim(nii.img, i);
  159. end
  160. end
  161. end
  162. % get index of orient (rotate inversely)
  163. %
  164. [tmp rot_orient] = sort(rot_orient);
  165. new_dim = old_dim;
  166. new_dim = new_dim(rot_orient);
  167. hdr.dime.dim([2:4]) = new_dim;
  168. new_pixdim = hdr.dime.pixdim([2:4]);
  169. new_pixdim = new_pixdim(rot_orient);
  170. hdr.dime.pixdim([2:4]) = new_pixdim;
  171. % re-calculate originator
  172. %
  173. tmp = hdr.hist.originator([1:3]);
  174. tmp = tmp(rot_orient);
  175. flip_orient = flip_orient(rot_orient);
  176. for i = 1:3
  177. if flip_orient(i) & ~isequal(tmp(i), 0)
  178. tmp(i) = new_dim(i) - tmp(i) + 1;
  179. end
  180. end
  181. hdr.hist.originator([1:3]) = tmp;
  182. hdr.hist.rot_orient = rot_orient;
  183. hdr.hist.flip_orient = flip_orient;
  184. % do rotation:
  185. %
  186. if ~isempty(pattern)
  187. pattern = permute(pattern, rot_orient);
  188. pattern = pattern(:);
  189. if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 | ...
  190. hdr.dime.datatype == 128 | hdr.dime.datatype == 511
  191. tmp = reshape(nii.img(:,:,:,1), [prod(new_dim) hdr.dime.dim(5:8)]);
  192. tmp = tmp(pattern, :);
  193. nii.img(:,:,:,1) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]);
  194. tmp = reshape(nii.img(:,:,:,2), [prod(new_dim) hdr.dime.dim(5:8)]);
  195. tmp = tmp(pattern, :);
  196. nii.img(:,:,:,2) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]);
  197. if hdr.dime.datatype == 128 | hdr.dime.datatype == 511
  198. tmp = reshape(nii.img(:,:,:,3), [prod(new_dim) hdr.dime.dim(5:8)]);
  199. tmp = tmp(pattern, :);
  200. nii.img(:,:,:,3) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]);
  201. end
  202. else
  203. nii.img = reshape(nii.img, [prod(new_dim) hdr.dime.dim(5:8)]);
  204. nii.img = nii.img(pattern, :);
  205. nii.img = reshape(nii.img, [new_dim hdr.dime.dim(5:8)]);
  206. end
  207. else
  208. if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 | ...
  209. hdr.dime.datatype == 128 | hdr.dime.datatype == 511
  210. nii.img(:,:,:,1) = permute(nii.img(:,:,:,1), rot_orient);
  211. nii.img(:,:,:,2) = permute(nii.img(:,:,:,2), rot_orient);
  212. if hdr.dime.datatype == 128 | hdr.dime.datatype == 511
  213. nii.img(:,:,:,3) = permute(nii.img(:,:,:,3), rot_orient);
  214. end
  215. else
  216. nii.img = permute(nii.img, rot_orient);
  217. end
  218. end
  219. else
  220. hdr.hist.rot_orient = [];
  221. hdr.hist.flip_orient = [];
  222. end
  223. nii.hdr = hdr;
  224. return; % xform_nii
  225. %-----------------------------------------------------------------------
  226. function [hdr, orient] = change_hdr(hdr, tolerance, preferredForm)
  227. orient = [1 2 3];
  228. affine_transform = 1;
  229. % NIFTI can have both sform and qform transform. This program
  230. % will check sform_code prior to qform_code by default.
  231. %
  232. % If user specifys "preferredForm", user can then choose the
  233. % priority. - Jeff
  234. %
  235. useForm=[]; % Jeff
  236. if isequal(preferredForm,'S')
  237. if isequal(hdr.hist.sform_code,0)
  238. error('User requires sform, sform not set in header');
  239. else
  240. useForm='s';
  241. end
  242. end % Jeff
  243. if isequal(preferredForm,'Q')
  244. if isequal(hdr.hist.qform_code,0)
  245. error('User requires qform, qform not set in header');
  246. else
  247. useForm='q';
  248. end
  249. end % Jeff
  250. if isequal(preferredForm,'s')
  251. if hdr.hist.sform_code > 0
  252. useForm='s';
  253. elseif hdr.hist.qform_code > 0
  254. useForm='q';
  255. end
  256. end % Jeff
  257. if isequal(preferredForm,'q')
  258. if hdr.hist.qform_code > 0
  259. useForm='q';
  260. elseif hdr.hist.sform_code > 0
  261. useForm='s';
  262. end
  263. end % Jeff
  264. if isequal(useForm,'s')
  265. R = [hdr.hist.srow_x(1:3)
  266. hdr.hist.srow_y(1:3)
  267. hdr.hist.srow_z(1:3)];
  268. T = [hdr.hist.srow_x(4)
  269. hdr.hist.srow_y(4)
  270. hdr.hist.srow_z(4)];
  271. if det(R) == 0 | ~isequal(R(find(R)), sum(R)')
  272. hdr.hist.old_affine = [ [R;[0 0 0]] [T;1] ];
  273. R_sort = sort(abs(R(:)));
  274. R( find( abs(R) < tolerance*min(R_sort(end-2:end)) ) ) = 0;
  275. hdr.hist.new_affine = [ [R;[0 0 0]] [T;1] ];
  276. if det(R) == 0 | ~isequal(R(find(R)), sum(R)')
  277. msg = [char(10) char(10) ' Non-orthogonal rotation or shearing '];
  278. msg = [msg 'found inside the affine matrix' char(10)];
  279. msg = [msg ' in this NIfTI file. You have 3 options:' char(10) char(10)];
  280. msg = [msg ' 1. Using included ''reslice_nii.m'' program to reslice the NIfTI' char(10)];
  281. msg = [msg ' file. I strongly recommand this, because it will not cause' char(10)];
  282. msg = [msg ' negative effect, as long as you remember not to do slice' char(10)];
  283. msg = [msg ' time correction after using ''reslice_nii.m''.' char(10) char(10)];
  284. msg = [msg ' 2. Using included ''load_untouch_nii.m'' program to load image' char(10)];
  285. msg = [msg ' without applying any affine geometric transformation or' char(10)];
  286. msg = [msg ' voxel intensity scaling. This is only for people who want' char(10)];
  287. msg = [msg ' to do some image processing regardless of image orientation' char(10)];
  288. msg = [msg ' and to save data back with the same NIfTI header.' char(10) char(10)];
  289. msg = [msg ' 3. Increasing the tolerance to allow more distortion in loaded' char(10)];
  290. msg = [msg ' image, but I don''t suggest this.' char(10) char(10)];
  291. msg = [msg ' To get help, please type:' char(10) char(10) ' help reslice_nii.m' char(10)];
  292. msg = [msg ' help load_untouch_nii.m' char(10) ' help load_nii.m'];
  293. error(msg);
  294. end
  295. end
  296. elseif isequal(useForm,'q')
  297. b = hdr.hist.quatern_b;
  298. c = hdr.hist.quatern_c;
  299. d = hdr.hist.quatern_d;
  300. if 1.0-(b*b+c*c+d*d) < 0
  301. if abs(1.0-(b*b+c*c+d*d)) < 1e-5
  302. a = 0;
  303. else
  304. error('Incorrect quaternion values in this NIFTI data.');
  305. end
  306. else
  307. a = sqrt(1.0-(b*b+c*c+d*d));
  308. end
  309. qfac = hdr.dime.pixdim(1);
  310. if qfac==0, qfac = 1; end
  311. i = hdr.dime.pixdim(2);
  312. j = hdr.dime.pixdim(3);
  313. k = qfac * hdr.dime.pixdim(4);
  314. R = [a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c
  315. 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b
  316. 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b];
  317. T = [hdr.hist.qoffset_x
  318. hdr.hist.qoffset_y
  319. hdr.hist.qoffset_z];
  320. % qforms are expected to generate rotation matrices R which are
  321. % det(R) = 1; we'll make sure that happens.
  322. %
  323. % now we make the same checks as were done above for sform data
  324. % BUT we do it on a transform that is in terms of voxels not mm;
  325. % after we figure out the angles and squash them to closest
  326. % rectilinear direction. After that, the voxel sizes are then
  327. % added.
  328. %
  329. % This part is modified by Jeff Gunter.
  330. %
  331. if det(R) == 0 | ~isequal(R(find(R)), sum(R)')
  332. % det(R) == 0 is not a common trigger for this ---
  333. % R(find(R)) is a list of non-zero elements in R; if that
  334. % is straight (not oblique) then it should be the same as
  335. % columnwise summation. Could just as well have checked the
  336. % lengths of R(find(R)) and sum(R)' (which should be 3)
  337. %
  338. hdr.hist.old_affine = [ [R * diag([i j k]);[0 0 0]] [T;1] ];
  339. R_sort = sort(abs(R(:)));
  340. R( find( abs(R) < tolerance*min(R_sort(end-2:end)) ) ) = 0;
  341. R = R * diag([i j k]);
  342. hdr.hist.new_affine = [ [R;[0 0 0]] [T;1] ];
  343. if det(R) == 0 | ~isequal(R(find(R)), sum(R)')
  344. msg = [char(10) char(10) ' Non-orthogonal rotation or shearing '];
  345. msg = [msg 'found inside the affine matrix' char(10)];
  346. msg = [msg ' in this NIfTI file. You have 3 options:' char(10) char(10)];
  347. msg = [msg ' 1. Using included ''reslice_nii.m'' program to reslice the NIfTI' char(10)];
  348. msg = [msg ' file. I strongly recommand this, because it will not cause' char(10)];
  349. msg = [msg ' negative effect, as long as you remember not to do slice' char(10)];
  350. msg = [msg ' time correction after using ''reslice_nii.m''.' char(10) char(10)];
  351. msg = [msg ' 2. Using included ''load_untouch_nii.m'' program to load image' char(10)];
  352. msg = [msg ' without applying any affine geometric transformation or' char(10)];
  353. msg = [msg ' voxel intensity scaling. This is only for people who want' char(10)];
  354. msg = [msg ' to do some image processing regardless of image orientation' char(10)];
  355. msg = [msg ' and to save data back with the same NIfTI header.' char(10) char(10)];
  356. msg = [msg ' 3. Increasing the tolerance to allow more distortion in loaded' char(10)];
  357. msg = [msg ' image, but I don''t suggest this.' char(10) char(10)];
  358. msg = [msg ' To get help, please type:' char(10) char(10) ' help reslice_nii.m' char(10)];
  359. msg = [msg ' help load_untouch_nii.m' char(10) ' help load_nii.m'];
  360. error(msg);
  361. end
  362. else
  363. R = R * diag([i j k]);
  364. end % 1st det(R)
  365. else
  366. affine_transform = 0; % no sform or qform transform
  367. end
  368. if affine_transform == 1
  369. voxel_size = abs(sum(R,1));
  370. inv_R = inv(R);
  371. originator = inv_R*(-T)+1;
  372. orient = get_orient(inv_R);
  373. % modify pixdim and originator
  374. %
  375. hdr.dime.pixdim(2:4) = voxel_size;
  376. hdr.hist.originator(1:3) = originator;
  377. % set sform or qform to non-use, because they have been
  378. % applied in xform_nii
  379. %
  380. hdr.hist.qform_code = 0;
  381. hdr.hist.sform_code = 0;
  382. end
  383. % apply space_unit to pixdim if not 1 (mm)
  384. %
  385. space_unit = get_units(hdr);
  386. if space_unit ~= 1
  387. hdr.dime.pixdim(2:4) = hdr.dime.pixdim(2:4) * space_unit;
  388. % set space_unit of xyzt_units to millimeter, because
  389. % voxel_size has been re-scaled
  390. %
  391. hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,1,0));
  392. hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,2,1));
  393. hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,3,0));
  394. end
  395. hdr.dime.pixdim = abs(hdr.dime.pixdim);
  396. return; % change_hdr
  397. %-----------------------------------------------------------------------
  398. function orient = get_orient(R)
  399. orient = [];
  400. for i = 1:3
  401. switch find(R(i,:)) * sign(sum(R(i,:)))
  402. case 1
  403. orient = [orient 1]; % Left to Right
  404. case 2
  405. orient = [orient 2]; % Posterior to Anterior
  406. case 3
  407. orient = [orient 3]; % Inferior to Superior
  408. case -1
  409. orient = [orient 4]; % Right to Left
  410. case -2
  411. orient = [orient 5]; % Anterior to Posterior
  412. case -3
  413. orient = [orient 6]; % Superior to Inferior
  414. end
  415. end
  416. return; % get_orient
  417. %-----------------------------------------------------------------------
  418. function [space_unit, time_unit] = get_units(hdr)
  419. switch bitand(hdr.dime.xyzt_units, 7) % mask with 0x07
  420. case 1
  421. space_unit = 1e+3; % meter, m
  422. case 3
  423. space_unit = 1e-3; % micrometer, um
  424. otherwise
  425. space_unit = 1; % millimeter, mm
  426. end
  427. switch bitand(hdr.dime.xyzt_units, 56) % mask with 0x38
  428. case 16
  429. time_unit = 1e-3; % millisecond, ms
  430. case 24
  431. time_unit = 1e-6; % microsecond, us
  432. otherwise
  433. time_unit = 1; % second, s
  434. end
  435. return; % get_units