|
| 1 | +function nii = jnii2nii(jnii, varargin) |
| 2 | +% |
| 3 | +% nii=jnii2nii(jnii) |
| 4 | +% or |
| 5 | +% nii=jnii2nii(jniifile) |
| 6 | +% jnii2nii(jniifile, niifile) |
| 7 | +% |
| 8 | +% Covert a JNIfTI file or data structure to a NIfTI-1/2 structure or file |
| 9 | +% |
| 10 | +% This function is compatible with both MATLAB and GNU Octave. |
| 11 | +% It accepts .jnii and .bnii input files |
| 12 | +% |
| 13 | +% author: Qianqian Fang (q.fang <at> neu.edu) |
| 14 | +% |
| 15 | +% input: |
| 16 | +% jnii: a JNIfTI data structure (a struct with NIFTIHeader and NIFTIData fields); |
| 17 | +% if jnii is a string, it represents a JNIfTI file (.jnii/.bnii) |
| 18 | +% niifile: if the 2nd parameter is given as a file name, the converted nifti data |
| 19 | +% will be save as a nii file with filename specified by niifile. |
| 20 | +% if the filename in niifile contains .gz, the file will be compressed using |
| 21 | +% the zmat toolbox. |
| 22 | +% |
| 23 | +% output: |
| 24 | +% nii: is the converted nifti-1/2 data structure, it contains the below subfields |
| 25 | +% nii.img: the data volume read from the nii file |
| 26 | +% nii.hdr: extended raw file header, a structure that is byte-wise compatible with a |
| 27 | +% nifti-1 - in this case, typecast(nii.hdr,'uint8') must be 348+4=352 bytes, |
| 28 | +% including the raw nifti-1 hdr header (348 bytes) plus the 4-byte |
| 29 | +% extension flags), or |
| 30 | +% nifti-2 - in this case, typecast(nii.hdr,'uint8') must be 540+4=544 bytes, |
| 31 | +% including the raw nifti-2 hdr header (540 bytes) plus the 4-byte |
| 32 | +% extension flags) |
| 33 | +% if one run nii.hdr.extension=[]; the resulting struct is 348/540-byte in length |
| 34 | +% nii.hdr key subfileds include |
| 35 | +% |
| 36 | +% sizeof_hdr: must be 348 (for NIFTI-1) or 540 (for NIFTI-2) |
| 37 | +% dim: short array, dim(2: dim(1)+1) defines the array size |
| 38 | +% datatype: the type of data stored in each voxel |
| 39 | +% bitpix: total bits per voxel |
| 40 | +% magic: must be 'ni1\0' or 'n+1\0' for NIFTI-1 data, and 'ni2\0' or 'n+2\0' for NIFTI-2 data |
| 41 | +% |
| 42 | +% For the detailed nii header, please see |
| 43 | +% https://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1.h |
| 44 | +% |
| 45 | +% dependency: |
| 46 | +% |
| 47 | +% To load a JNIfTI file with compression or niifile ends with (.nii.gz/.hdr.gz/.img.gz), |
| 48 | +% one must install the ZMat Toolbox (https://github.com/NeuroJSON/zmat) and |
| 49 | +% JSONLab Toolbox (https://github.com/NeuroJSON/jsonlab); |
| 50 | +% |
| 51 | +% this file is part of JNIfTI specification: https://github.com/NeuroJSON/jnifty |
| 52 | +% |
| 53 | +% License: Apache 2.0, see https://github.com/NeuroJSON/jnifty for details |
| 54 | +% |
| 55 | + |
| 56 | +if (nargin <= 0) |
| 57 | + help jnii2nii; |
| 58 | + return |
| 59 | +end |
| 60 | + |
| 61 | +if (~isstruct(jnii)) |
| 62 | + jnii = loadjnifti(jnii); |
| 63 | +end |
| 64 | + |
| 65 | +if (~(isfield(jnii, 'NIFTIHeader') && isfield(jnii, 'NIFTIData'))) |
| 66 | + error('input must be a valid JNIfTI structure (needs both NIFTIHeader and NIFTIData subfields)'); |
| 67 | +end |
| 68 | + |
| 69 | +niiformat = 'nifti1'; |
| 70 | + |
| 71 | +if ((isfield(jnii.NIFTIHeader, 'NIIFormat') && ismember(jnii.NIFTIHeader.NIIFormat(1:3), {'ni2', 'n+2'})) || max(jnii.NIFTIHeader.Dim) >= 2^32) |
| 72 | + niiformat = 'nifti2'; |
| 73 | +end |
| 74 | + |
| 75 | +nii.hdr = nifticreate(jnii.NIFTIData, niiformat); |
| 76 | +nii.img = jnii.NIFTIData; |
| 77 | + |
| 78 | +if (isfield(jnii.NIFTIHeader, 'NIIHeaderSize')) |
| 79 | + nii.hdr.sizeof_hdr = bytematch(jnii.NIFTIHeader, 'NIIHeaderSize', nii.hdr.sizeof_hdr); |
| 80 | +end |
| 81 | +if (isfield(nii.hdr, 'data_type')) |
| 82 | + nii.hdr.data_type = bytematch(jnii.NIFTIHeader, 'A75DataTypeName', nii.hdr.data_type); |
| 83 | + nii.hdr.db_name = bytematch(jnii.NIFTIHeader, 'A75DBName', nii.hdr.db_name); |
| 84 | + nii.hdr.extents = bytematch(jnii.NIFTIHeader, 'A75Extends', nii.hdr.extents); |
| 85 | + nii.hdr.session_error = bytematch(jnii.NIFTIHeader, 'A75SessionError', nii.hdr.session_error); |
| 86 | + nii.hdr.regular = bytematch(jnii.NIFTIHeader, 'A75Regular', nii.hdr.regular); |
| 87 | +end |
| 88 | + |
| 89 | +dim_info = bitor(uint8(jnii.NIFTIHeader.DimInfo.Freq), bitshift(uint8(jnii.NIFTIHeader.DimInfo.Phase), 3)); |
| 90 | +dim_info = bitor(dim_info, bitshift(uint8(jnii.NIFTIHeader.DimInfo.Slice), 6)); |
| 91 | +nii.hdr.dim_info = cast(dim_info, class(nii.hdr.dim_info)); |
| 92 | + |
| 93 | +nii.hdr.dim(1) = cast(length(jnii.NIFTIHeader.Dim), class(nii.hdr.dim)); |
| 94 | +nii.hdr.dim(2:1 + length(jnii.NIFTIHeader.Dim)) = bytematch(jnii.NIFTIHeader, 'Dim', nii.hdr.dim(2:1 + length(jnii.NIFTIHeader.Dim))); |
| 95 | +nii.hdr.intent_p1 = bytematch(jnii.NIFTIHeader, 'Param1', nii.hdr.intent_p1); |
| 96 | +nii.hdr.intent_p2 = bytematch(jnii.NIFTIHeader, 'Param2', nii.hdr.intent_p2); |
| 97 | +nii.hdr.intent_p3 = bytematch(jnii.NIFTIHeader, 'Param3', nii.hdr.intent_p3); |
| 98 | + |
| 99 | +if (isfield(jnii.NIFTIHeader, 'Intent') && ischar(jnii.NIFTIHeader.Intent)) |
| 100 | + jnii.NIFTIHeader.Intent = niicodemap('intent', jnii.NIFTIHeader.Intent); |
| 101 | +end |
| 102 | +nii.hdr.intent_code = bytematch(jnii.NIFTIHeader, 'Intent', nii.hdr.intent_code); |
| 103 | +if (isfield(jnii.NIFTIHeader, 'DataType') && ischar(jnii.NIFTIHeader.DataType)) |
| 104 | + jnii.NIFTIHeader.DataType = niicodemap('datatype', jnii.NIFTIHeader.DataType); |
| 105 | +end |
| 106 | + |
| 107 | +nii.hdr.datatype = bytematch(jnii.NIFTIHeader, 'DataType', nii.hdr.datatype); |
| 108 | +nii.hdr.bitpix = bytematch(jnii.NIFTIHeader, 'BitDepth', nii.hdr.bitpix); |
| 109 | +nii.hdr.slice_start = bytematch(jnii.NIFTIHeader, 'FirstSliceID', nii.hdr.slice_start); |
| 110 | +nii.hdr.pixdim(1) = cast(length(jnii.NIFTIHeader.VoxelSize), class(nii.hdr.pixdim)); |
| 111 | +nii.hdr.pixdim(2:2 + nii.hdr.dim(1) - 1) = bytematch(jnii.NIFTIHeader, 'VoxelSize', nii.hdr.pixdim(2:2 + nii.hdr.dim(1) - 1)); |
| 112 | +nii.hdr.vox_offset = bytematch(jnii.NIFTIHeader, 'NIIByteOffset', nii.hdr.vox_offset); |
| 113 | +nii.hdr.scl_slope = bytematch(jnii.NIFTIHeader, 'ScaleSlope', nii.hdr.scl_slope); |
| 114 | +nii.hdr.scl_inter = bytematch(jnii.NIFTIHeader, 'ScaleOffset', nii.hdr.scl_inter); |
| 115 | +nii.hdr.slice_end = bytematch(jnii.NIFTIHeader, 'LastSliceID', nii.hdr.slice_end); |
| 116 | + |
| 117 | +if (isfield(jnii.NIFTIHeader, 'SliceType') && ischar(jnii.NIFTIHeader.SliceType)) |
| 118 | + jnii.NIFTIHeader.SliceType = niicodemap('slicetype', jnii.NIFTIHeader.SliceType); |
| 119 | +end |
| 120 | +nii.hdr.slice_code = bytematch(jnii.NIFTIHeader, 'SliceType', nii.hdr.slice_code); |
| 121 | +if (isfield(jnii.NIFTIHeader, 'Unit') && isfield(jnii.NIFTIHeader.Unit, 'L') && ischar(jnii.NIFTIHeader.Unit.L)) |
| 122 | + jnii.NIFTIHeader.Unit.L = niicodemap('unit', jnii.NIFTIHeader.Unit.L); |
| 123 | +end |
| 124 | +if (isfield(jnii.NIFTIHeader, 'Unit') && isfield(jnii.NIFTIHeader.Unit, 'T') && ischar(jnii.NIFTIHeader.Unit.T)) |
| 125 | + jnii.NIFTIHeader.Unit.T = niicodemap('unit', jnii.NIFTIHeader.Unit.T); |
| 126 | +end |
| 127 | + |
| 128 | +xyzt_units = bitor(uint8(jnii.NIFTIHeader.Unit.L), uint8(jnii.NIFTIHeader.Unit.T)); |
| 129 | +nii.hdr.xyzt_units = cast(xyzt_units, class(nii.hdr.xyzt_units)); |
| 130 | + |
| 131 | +nii.hdr.cal_max = bytematch(jnii.NIFTIHeader, 'MaxIntensity', nii.hdr.cal_max); |
| 132 | +nii.hdr.cal_min = bytematch(jnii.NIFTIHeader, 'MinIntensity', nii.hdr.cal_min); |
| 133 | +nii.hdr.slice_duration = bytematch(jnii.NIFTIHeader, 'SliceTime', nii.hdr.slice_duration); |
| 134 | +nii.hdr.toffset = bytematch(jnii.NIFTIHeader, 'TimeOffset', nii.hdr.toffset); |
| 135 | +if (isfield(nii.hdr, 'glmax')) |
| 136 | + nii.hdr.glmax = bytematch(jnii.NIFTIHeader, 'A75GlobalMax', nii.hdr.glmax); |
| 137 | + nii.hdr.glmin = bytematch(jnii.NIFTIHeader, 'A75GlobalMin', nii.hdr.glmin); |
| 138 | +end |
| 139 | + |
| 140 | +nii.hdr.descrip = bytematch(jnii.NIFTIHeader, 'Description', nii.hdr.descrip); |
| 141 | +nii.hdr.aux_file = bytematch(jnii.NIFTIHeader, 'AuxFile', nii.hdr.aux_file); |
| 142 | + |
| 143 | +if (isfield(jnii.NIFTIHeader, 'QForm') && ischar(jnii.NIFTIHeader.QForm)) |
| 144 | + jnii.NIFTIHeader.QForm = niicodemap('qform_code', jnii.NIFTIHeader.QForm); |
| 145 | +end |
| 146 | +if (isfield(jnii.NIFTIHeader, 'SForm') && ischar(jnii.NIFTIHeader.SForm)) |
| 147 | + jnii.NIFTIHeader.SForm = niicodemap('sform_code', jnii.NIFTIHeader.SForm); |
| 148 | +end |
| 149 | + |
| 150 | +nii.hdr.qform_code = bytematch(jnii.NIFTIHeader, 'QForm', nii.hdr.qform_code); |
| 151 | +nii.hdr.sform_code = bytematch(jnii.NIFTIHeader, 'SForm', nii.hdr.sform_code); |
| 152 | +nii.hdr.quatern_b = bytematch(jnii.NIFTIHeader, 'Quatern.b', nii.hdr.quatern_b); |
| 153 | +nii.hdr.quatern_c = bytematch(jnii.NIFTIHeader, 'Quatern.c', nii.hdr.quatern_c); |
| 154 | +nii.hdr.quatern_d = bytematch(jnii.NIFTIHeader, 'Quatern.d', nii.hdr.quatern_d); |
| 155 | +nii.hdr.qoffset_x = bytematch(jnii.NIFTIHeader, 'QuaternOffset.x', nii.hdr.qoffset_x); |
| 156 | +nii.hdr.qoffset_y = bytematch(jnii.NIFTIHeader, 'QuaternOffset.y', nii.hdr.qoffset_y); |
| 157 | +nii.hdr.qoffset_z = bytematch(jnii.NIFTIHeader, 'QuaternOffset.z', nii.hdr.qoffset_z); |
| 158 | +nii.hdr.srow_x = cast(jnii.NIFTIHeader.Affine(1, :), class(nii.hdr.srow_x)); |
| 159 | +nii.hdr.srow_y = cast(jnii.NIFTIHeader.Affine(2, :), class(nii.hdr.srow_y)); |
| 160 | +nii.hdr.srow_z = cast(jnii.NIFTIHeader.Affine(3, :), class(nii.hdr.srow_z)); |
| 161 | + |
| 162 | +nii.hdr.intent_name = bytematch(jnii.NIFTIHeader, 'Name', nii.hdr.intent_name); |
| 163 | +% nii.hdr.magic =bytematch(jnii.NIFTIHeader, 'NIIFormat', nii.hdr.magic); |
| 164 | + |
| 165 | +if (isfield(jnii.NIFTIHeader, 'NIIExtender')) |
| 166 | + nii.hdr.extension = bytematch(jnii.NIFTIHeader, 'NIIExtender', nii.hdr.extension); |
| 167 | +end |
| 168 | +if (isfield(jnii.NIFTIHeader, 'NIIQfac_')) |
| 169 | + nii.hdr.pixdim(1) = bytematch(jnii.NIFTIHeader, 'NIIQfac_', nii.hdr.pixdim(1)); |
| 170 | +end |
| 171 | +if (isfield(jnii.NIFTIHeader, 'NIIUnused_')) |
| 172 | + nii.hdr.reserved = bytematch(jnii.NIFTIHeader, 'NIIUnused_', nii.hdr.reserved); |
| 173 | +end |
| 174 | + |
| 175 | +if (isfield(jnii, 'NIFTIExtension') && iscell(jnii.NIFTIExtension)) |
| 176 | + nii.extension = jnii.NIFTIExtension; |
| 177 | + if (nii.hdr.extension(1) ~= length(jnii.NIFTIExtension)) |
| 178 | + nii.hdr.extension(1) = length(jnii.NIFTIExtension); |
| 179 | + warning('header extension count does not match the extension data, force update'); |
| 180 | + end |
| 181 | +end |
| 182 | + |
| 183 | +if (nargin >= 2 && ischar(varargin{1})) |
| 184 | + savenifti(nii.img, varargin{1}, nii.hdr); |
| 185 | +end |
| 186 | + |
| 187 | +% --------------------------------------------------------------------------- |
| 188 | + |
| 189 | +function dat = bytematch(jobj, key, orig) |
| 190 | +dtype = class(orig); |
| 191 | +if (isfield(jobj, key)) |
| 192 | + dat = cast(jobj.(key), dtype); |
| 193 | +else |
| 194 | + dat = cast(0, dtype); |
| 195 | +end |
| 196 | +if (length(dat) < length(orig)) |
| 197 | + dat(length(orig)) = cast(0, dtype); |
| 198 | +end |
| 199 | +dat = dat(1:length(orig)); |
0 commit comments