Skip to content

Commit 19728df

Browse files
committed
[jnifty] import NIfTI/JNIfTI writers from jnifty (savenifti, jnii2nii, etc.)
1 parent 85ee54b commit 19728df

10 files changed

Lines changed: 600 additions & 0 deletions

File tree

‎Contents.m‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@
5757
% loadjnifti - jnii=loadjnifti(inputfile)
5858
% loadnifti - jnii=loadnifti(inputfile)
5959
% nii2jnii - jnii=nii2jnii(niifile)
60+
% jnii2nii - nii=jnii2nii(jnii)
6061
% niicodemap - newval=niicodemap(name, value)
6162
% niiformat - niiheader=niiformat(format)
6263
% niiheader2jnii - nii = niiheader2jnii(nii0)
64+
% nifticreate - header=nifticreate(img)
65+
% jnifticreate - jnii=jnifticreate(img,'header1',value1,...)
66+
% savenifti - savenifti(img, filename)
67+
% savejnifti - savejnifti(jnii, outputfile)
68+
% savejnii - savejnii(jniidata, jniifile)
69+
% savebnii - savebnii(jniidata, bniifile)

‎INDEX‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,16 @@ JNIfTI file parser
5757
loadjnifti
5858
loadnifti
5959
nii2jnii
60+
jnii2nii
6061
niicodemap
6162
niiformat
6263
niiheader2jnii
64+
nifticreate
65+
jnifticreate
66+
savenifti
67+
savejnifti
68+
savejnii
69+
savebnii
6370
Helper Functions
6471
decodevarname
6572
encodevarname

‎jnifticreate.m‎

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
function jnii = jnifticreate(varargin)
2+
%
3+
% jnii=jnifticreate
4+
% or
5+
% jnii=jnifticreate('header1', value1, 'header2', value2, ...)
6+
% jnii=jnifticreate(img, 'header1', value1, ...)
7+
%
8+
% Create a default JNIfTI structure with default header and image volume
9+
%
10+
% author: Qianqian Fang (q.fang <at> neu.edu)
11+
%
12+
% input:
13+
% img: set the jnii.NIFTIData section
14+
% 'header_i': the header subfield name defined in the JNIfTI
15+
% specification, see https://github.com/NeuroJSON/jnifty
16+
% value_i: set the value for the specified JNIfTI header field
17+
%
18+
% output:
19+
% jnii: without any input, jnii gives the default jnii header
20+
% if img is given, jnii also includes the NIFTIData field
21+
%
22+
%
23+
% this file is part of JNIfTI specification: https://github.com/NeuroJSON/jnifty
24+
%
25+
% License: Apache 2.0, see https://github.com/NeuroJSON/jnifty for details
26+
%
27+
28+
jnii = struct(encodevarname('_DataInfo_'), struct(), 'NIFTIHeader', struct(), 'NIFTIData', []);
29+
30+
% jnii.NIFTIHeader.NIIHeaderSize= 0;
31+
% jnii.NIFTIHeader.A75DataTypeName= 'uint8';
32+
% jnii.NIFTIHeader.A75DBName= '';
33+
% jnii.NIFTIHeader.A75Extends= 0;
34+
% jnii.NIFTIHeader.A75SessionError='';
35+
% jnii.NIFTIHeader.A75Regular= 0;
36+
jnii.NIFTIHeader.DimInfo.Freq = 0;
37+
jnii.NIFTIHeader.DimInfo.Phase = 0;
38+
jnii.NIFTIHeader.DimInfo.Slice = 0;
39+
jnii.NIFTIHeader.Dim = [];
40+
jnii.NIFTIHeader.Param1 = 0;
41+
jnii.NIFTIHeader.Param2 = 0;
42+
jnii.NIFTIHeader.Param3 = 0;
43+
jnii.NIFTIHeader.Intent = '';
44+
jnii.NIFTIHeader.DataType = 'uint8';
45+
jnii.NIFTIHeader.BitDepth = 8;
46+
jnii.NIFTIHeader.FirstSliceID = 1;
47+
jnii.NIFTIHeader.VoxelSize = [1, 1, 1, 1];
48+
jnii.NIFTIHeader.Orientation = struct('x', 'r', 'y', 'a', 'z', 's');
49+
% jnii.NIFTIHeader.NIIByteOffset= 0;
50+
jnii.NIFTIHeader.ScaleSlope = 0;
51+
jnii.NIFTIHeader.ScaleOffset = 0;
52+
jnii.NIFTIHeader.LastSliceID = 1;
53+
jnii.NIFTIHeader.SliceType = '';
54+
jnii.NIFTIHeader.Unit = struct('L', 'mm', 'T', 's');
55+
jnii.NIFTIHeader.MaxIntensity = 255;
56+
jnii.NIFTIHeader.MinIntensity = 0;
57+
jnii.NIFTIHeader.SliceTime = 1;
58+
jnii.NIFTIHeader.TimeOffset = 0;
59+
% jnii.NIFTIHeader.A75GlobalMax= 255;
60+
% jnii.NIFTIHeader.A75GlobalMin= 0;
61+
jnii.NIFTIHeader.Description = '';
62+
% jnii.NIFTIHeader.AuxFile= '';
63+
jnii.NIFTIHeader.QForm = '';
64+
jnii.NIFTIHeader.SForm = 'scanner_anat';
65+
jnii.NIFTIHeader.Quatern.b = 0;
66+
jnii.NIFTIHeader.Quatern.c = 0;
67+
jnii.NIFTIHeader.Quatern.d = 0;
68+
jnii.NIFTIHeader.QuaternOffset.x = 0;
69+
jnii.NIFTIHeader.QuaternOffset.y = 0;
70+
jnii.NIFTIHeader.QuaternOffset.z = 0;
71+
jnii.NIFTIHeader.Affine(1, :) = [1 0 0 0];
72+
jnii.NIFTIHeader.Affine(2, :) = [0 1 0 0];
73+
jnii.NIFTIHeader.Affine(3, :) = [0 0 1 0];
74+
jnii.NIFTIHeader.Name = 'default';
75+
jnii.NIFTIHeader.NIIFormat = 'jnifti';
76+
% jnii.NIFTIHeader.NIIExtender= [0,0,0,0];
77+
78+
datainfo.JNIFTIVersion = '0.5';
79+
datainfo.Comment = 'Created by JNIFTY Toolbox (https://github.com/NeuroJSON/jnifty)';
80+
datainfo.AnnotationFormat = 'https://neurojson.org/jnifti/draft2';
81+
datainfo.SerialFormat = 'https://json.org';
82+
datainfo.Parser = struct('Python', [], ...
83+
'MATLAB', [], ...
84+
'JavaScript', 'https://github.com/NeuroJSON/jsdata', ...
85+
'CPP', 'https://github.com/NeuroJSON/json', ...
86+
'C', 'https://github.com/NeuroJSON/ubj');
87+
datainfo.Parser.Python = {'https://pypi.org/project/jdata', 'https://pypi.org/project/bjdata'};
88+
datainfo.Parser.MATLAB = {'https://github.com/NeuroJSON/jnifty', 'https://github.com/NeuroJSON/jsonlab'};
89+
jnii.(encodevarname('_DataInfo_')) = datainfo;
90+
91+
if (nargin == 0)
92+
return
93+
end
94+
95+
img = [];
96+
pid = 1;
97+
if (~ischar(varargin{1}))
98+
img = varargin{1};
99+
pid = 2;
100+
end
101+
102+
if (~isempty(varargin))
103+
for i = pid:2:length(varargin)
104+
jnii.NIFTIHeader.(varargin{i}) = varargin{i + 1};
105+
end
106+
end
107+
108+
if (~isnumeric(img) && ~islogical(img))
109+
error('img input must be a numerical or logical array');
110+
end
111+
112+
jnii.NIFTIHeader.Dim = size(img);
113+
jnii.NIFTIHeader.DataType = class(img);
114+
info = whos('img');
115+
jnii.NIFTIHeader.BitDepth = info.bytes / numel(img) * 8;
116+
jnii.NIFTIHeader.MinIntensity = min(img(:));
117+
jnii.NIFTIHeader.MaxIntensity = max(img(:));
118+
119+
jnii.NIFTIData = img;

‎jnii2nii.m‎

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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));

‎jsonlab.prj‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,16 @@ Please note that data files produced by `saveubjson` may utilize a special "opti
158158
<file>${PROJECT_ROOT}/loadnifti.m</file>
159159
<file>${PROJECT_ROOT}/memmapstream.m</file>
160160
<file>${PROJECT_ROOT}/nii2jnii.m</file>
161+
<file>${PROJECT_ROOT}/jnii2nii.m</file>
161162
<file>${PROJECT_ROOT}/niicodemap.m</file>
162163
<file>${PROJECT_ROOT}/niiformat.m</file>
163164
<file>${PROJECT_ROOT}/niiheader2jnii.m</file>
165+
<file>${PROJECT_ROOT}/nifticreate.m</file>
166+
<file>${PROJECT_ROOT}/jnifticreate.m</file>
167+
<file>${PROJECT_ROOT}/savenifti.m</file>
168+
<file>${PROJECT_ROOT}/savejnifti.m</file>
169+
<file>${PROJECT_ROOT}/savejnii.m</file>
170+
<file>${PROJECT_ROOT}/savebnii.m</file>
164171
<file>${PROJECT_ROOT}/regrouph5.m</file>
165172
<file>${PROJECT_ROOT}/transposemat.m</file>
166173
</fileset.rootfiles>

0 commit comments

Comments
 (0)