| function ft_write_cifti(filename, source, varargin) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| parameter = ft_getopt(varargin, 'parameter'); |
| brainstructure = ft_getopt(varargin, 'brainstructure'); |
| parcellation = ft_getopt(varargin, 'parcellation'); |
| precision = ft_getopt(varargin, 'precision', 'single'); |
| writesurface = ft_getopt(varargin, 'writesurface', true); |
| debug = ft_getopt(varargin, 'debug', false); |
|
|
| if isfield(source, 'brainordinate') |
| |
| |
| source = copyfields(source.brainordinate, source, fieldnames(source.brainordinate)); |
| source = rmfield(source, 'brainordinate'); |
| end |
|
|
| if isempty(brainstructure) && isfield(source, 'brainstructure') && isfield(source, 'brainstructurelabel') |
| |
| brainstructure = 'brainstructure'; |
| end |
|
|
| if isempty(parcellation) && isfield(source, 'parcellation') && isfield(source, 'parcellationlabel') |
| |
| parcellation = 'parcellation'; |
| end |
|
|
| if isfield(source, 'inside') && islogical(source.inside) |
| |
| source.inside = find(source.inside(:)); |
| end |
|
|
| if isfield(source, 'dim') && ~isfield(source, 'transform') |
| |
| source.transform = pos2transform(source.pos); |
| end |
|
|
| |
| |
| |
|
|
| dat = source.(parameter); |
| dimord = getdimord(source, parameter); |
|
|
| switch dimord |
| case {'pos' 'pos_scalar'} |
| |
| extension = '.dscalar.nii'; |
| intent_code = 3006; |
| intent_name = 'ConnDenseScalar'; |
| dat = transpose(dat); |
| dimord = 'scalar_pos'; |
| case 'pos_pos' |
| |
| extension = '.dconn.nii'; |
| intent_code = 3001; |
| intent_name = 'ConnDense'; |
| case 'pos_time' |
| |
| extension = '.dtseries.nii'; |
| intent_code = 3002; |
| intent_name = 'ConnDenseSeries'; |
| dat = transpose(dat); |
| dimord = 'time_pos'; |
| case 'pos_freq' |
| |
| extension = '.dtseries.nii'; |
| intent_code = 3002; |
| intent_name = 'ConnDenseSeries'; |
| dat = transpose(dat); |
| dimord = 'freq_pos'; |
| |
| case {'chan' 'chan_scalar'} |
| |
| extension = '.pscalar.nii'; |
| intent_code = 3006; |
| intent_name = 'ConnParcelScalr'; |
| dat = transpose(dat); |
| dimord = 'scalar_chan'; |
| case 'chan_chan' |
| |
| extension = '.pconn.nii'; |
| intent_code = 3003; |
| intent_name = 'ConnParcels'; |
| case 'chan_time' |
| |
| extension = '.ptseries.nii'; |
| intent_code = 3004; |
| intent_name = 'ConnParcelSries'; |
| dat = transpose(dat); |
| dimord = 'time_chan'; |
| case 'chan_freq' |
| |
| extension = '.ptseries.nii'; |
| intent_code = 3004; |
| intent_name = 'ConnParcelSries'; |
| dat = transpose(dat); |
| dimord = 'freq_chan'; |
| |
| case {'chan_chan_time' 'chan_chan_freq'} |
| |
| extension = '.pconnseries.nii'; |
| intent_code = 3011; |
| intent_name = 'ConnPPSr'; |
| |
| case {'pos_pos_time' 'pos_pos_freq'} |
| |
| extension = '.dconnseries.nii'; |
| intent_code = 3000; |
| intent_name = 'ConnUnknown'; |
| |
| otherwise |
| error('unsupported dimord "%s"', dimord); |
| end |
|
|
| |
| dimtok = tokenize(dimord, '_'); |
|
|
| [p, f, x] = fileparts(filename); |
| if isequal(x, '.nii') |
| filename = fullfile(p, f); |
| end |
|
|
| [p, f, x] = fileparts(filename); |
| if any(isequal(x, {'.dtseries', '.ptseries', '.dconn', '.pconn', '.dscalar', '.pscalar'})) |
| filename = fullfile(p, f); |
| end |
|
|
| |
| [p, f, x] = fileparts(filename); |
| filename = fullfile(p, [f x extension]); |
|
|
| |
| |
| |
|
|
| ModelType = zeros(size(source.pos,1), 1); |
| ModelTypelabel = {'SURFACE', 'VOXEL'}; |
| if isfield(source, 'transform') |
| tolerance = 0.01; |
| ijk = ft_warp_apply(inv(source.transform), source.pos); |
| sel = sqrt(sum((ijk - round(ijk)).^2,2))<tolerance; |
| |
| ModelType(~sel) = 1; |
| ModelType( sel) = 2; |
| else |
| ModelType(:) = 1; |
| end |
|
|
| if isfield(source, brainstructure) |
| BrainStructure = source.( brainstructure ); |
| BrainStructurelabel = source.([brainstructure 'label']); |
| elseif isfield(source, 'pos') |
| BrainStructure = ones(size(source.pos,1),1); |
| BrainStructurelabel = {'INVALID'}; |
| end |
|
|
| if isfield(source, parcellation) |
| Parcellation = source.( parcellation ); |
| Parcellationlabel = source.([parcellation 'label']); |
| elseif isfield(source, 'pos') |
| Parcellation = ones(size(source.pos,1),1); |
| Parcellationlabel = {'INVALID'}; |
| end |
|
|
| |
| try, BrainStructure = BrainStructure(:); end |
| try, Parcellation = Parcellation(:); end |
|
|
| list1 = { |
| 'CIFTI_STRUCTURE_CORTEX_LEFT' |
| 'CIFTI_STRUCTURE_CORTEX_RIGHT' |
| 'CIFTI_STRUCTURE_CEREBELLUM' |
| 'CIFTI_STRUCTURE_ACCUMBENS_LEFT' |
| 'CIFTI_STRUCTURE_ACCUMBENS_RIGHT' |
| 'CIFTI_STRUCTURE_ALL_GREY_MATTER' |
| 'CIFTI_STRUCTURE_ALL_WHITE_MATTER' |
| 'CIFTI_STRUCTURE_AMYGDALA_LEFT' |
| 'CIFTI_STRUCTURE_AMYGDALA_RIGHT' |
| 'CIFTI_STRUCTURE_BRAIN_STEM' |
| 'CIFTI_STRUCTURE_CAUDATE_LEFT' |
| 'CIFTI_STRUCTURE_CAUDATE_RIGHT' |
| 'CIFTI_STRUCTURE_CEREBELLAR_WHITE_MATTER_LEFT' |
| 'CIFTI_STRUCTURE_CEREBELLAR_WHITE_MATTER_RIGHT' |
| 'CIFTI_STRUCTURE_CEREBELLUM_LEFT' |
| 'CIFTI_STRUCTURE_CEREBELLUM_RIGHT' |
| 'CIFTI_STRUCTURE_CEREBRAL_WHITE_MATTER_LEFT' |
| 'CIFTI_STRUCTURE_CEREBRAL_WHITE_MATTER_RIGHT' |
| 'CIFTI_STRUCTURE_CORTEX' |
| 'CIFTI_STRUCTURE_DIENCEPHALON_VENTRAL_LEFT' |
| 'CIFTI_STRUCTURE_DIENCEPHALON_VENTRAL_RIGHT' |
| 'CIFTI_STRUCTURE_HIPPOCAMPUS_LEFT' |
| 'CIFTI_STRUCTURE_HIPPOCAMPUS_RIGHT' |
| 'CIFTI_STRUCTURE_INVALID' |
| 'CIFTI_STRUCTURE_OTHER' |
| 'CIFTI_STRUCTURE_OTHER_GREY_MATTER' |
| 'CIFTI_STRUCTURE_OTHER_WHITE_MATTER' |
| 'CIFTI_STRUCTURE_PALLIDUM_LEFT' |
| 'CIFTI_STRUCTURE_PALLIDUM_RIGHT' |
| 'CIFTI_STRUCTURE_PUTAMEN_LEFT' |
| 'CIFTI_STRUCTURE_PUTAMEN_RIGHT' |
| 'CIFTI_STRUCTURE_THALAMUS_LEFT' |
| 'CIFTI_STRUCTURE_THALAMUS_RIGHT' |
| }; |
|
|
| list2 = { |
| 'CORTEX_LEFT' |
| 'CORTEX_RIGHT' |
| 'CEREBELLUM' |
| 'ACCUMBENS_LEFT' |
| 'ACCUMBENS_RIGHT' |
| 'ALL_GREY_MATTER' |
| 'ALL_WHITE_MATTER' |
| 'AMYGDALA_LEFT' |
| 'AMYGDALA_RIGHT' |
| 'BRAIN_STEM' |
| 'CAUDATE_LEFT' |
| 'CAUDATE_RIGHT' |
| 'CEREBELLAR_WHITE_MATTER_LEFT' |
| 'CEREBELLAR_WHITE_MATTER_RIGHT' |
| 'CEREBELLUM_LEFT' |
| 'CEREBELLUM_RIGHT' |
| 'CEREBRAL_WHITE_MATTER_LEFT' |
| 'CEREBRAL_WHITE_MATTER_RIGHT' |
| 'CORTEX' |
| 'DIENCEPHALON_VENTRAL_LEFT' |
| 'DIENCEPHALON_VENTRAL_RIGHT' |
| 'HIPPOCAMPUS_LEFT' |
| 'HIPPOCAMPUS_RIGHT' |
| 'INVALID' |
| 'OTHER' |
| 'OTHER_GREY_MATTER' |
| 'OTHER_WHITE_MATTER' |
| 'PALLIDUM_LEFT' |
| 'PALLIDUM_RIGHT' |
| 'PUTAMEN_LEFT' |
| 'PUTAMEN_RIGHT' |
| 'THALAMUS_LEFT' |
| 'THALAMUS_RIGHT' |
| }; |
|
|
| |
| [dum, indx1, indx2] = intersect(BrainStructurelabel, list2); |
| BrainStructurelabel(indx1) = list1(indx2); |
|
|
| |
| |
| |
|
|
| |
| ft_hastoolbox('gifti', 1); |
|
|
| tree = xmltree; |
| tree = set(tree, 1, 'name', 'CIFTI'); |
| tree = attributes(tree, 'add', find(tree, 'CIFTI'), 'Version', '2'); |
| tree = add(tree, find(tree, 'CIFTI'), 'element', 'Matrix'); |
|
|
| |
| |
| |
| |
| |
| |
|
|
| if any(strcmp(dimtok, 'time')) |
| |
| |
| tree = add(tree, find(tree, 'CIFTI/Matrix'), 'element', 'MatrixIndicesMap'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap'); |
| branch = branch(end); |
| if length(source.time)>1 |
| SeriesStep = median(diff(source.time)); |
| else |
| SeriesStep = 0; |
| end |
| tree = attributes(tree, 'add', branch, 'AppliesToMatrixDimension', printwithcomma(find(strcmp(dimtok, 'time'))-1)); |
| tree = attributes(tree, 'add', branch, 'IndicesMapToDataType', 'CIFTI_INDEX_TYPE_SERIES'); |
| tree = attributes(tree, 'add', branch, 'NumberOfSeriesPoints', num2str(length(source.time))); |
| tree = attributes(tree, 'add', branch, 'SeriesExponent', num2str(0)); |
| tree = attributes(tree, 'add', branch, 'SeriesStart', num2str(source.time(1))); |
| tree = attributes(tree, 'add', branch, 'SeriesStep', num2str(SeriesStep)); |
| tree = attributes(tree, 'add', branch, 'SeriesUnit', 'SECOND'); |
| end |
|
|
| if any(strcmp(dimtok, 'freq')) |
| |
| |
| tree = add(tree, find(tree, 'CIFTI/Matrix'), 'element', 'MatrixIndicesMap'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap'); |
| branch = branch(end); |
| if length(source.freq)>1 |
| SeriesStep = median(diff(source.freq)); |
| else |
| SeriesStep = 0; |
| end |
| tree = attributes(tree, 'add', branch, 'AppliesToMatrixDimension', printwithcomma(find(strcmp(dimtok, 'freq'))-1)); |
| tree = attributes(tree, 'add', branch, 'IndicesMapToDataType', 'CIFTI_INDEX_TYPE_SCALARS'); |
| tree = attributes(tree, 'add', branch, 'NumberOfSeriesPoints', num2str(length(source.freq))); |
| tree = attributes(tree, 'add', branch, 'SeriesExponent', num2str(0)); |
| tree = attributes(tree, 'add', branch, 'SeriesStart', num2str(source.freq(1))); |
| tree = attributes(tree, 'add', branch, 'SeriesStep', num2str(SeriesStep)); |
| tree = attributes(tree, 'add', branch, 'SeriesUnit', 'HZ'); |
| end |
|
|
| if any(strcmp(dimtok, 'scalar')) |
| tree = add(tree, find(tree, 'CIFTI/Matrix'), 'element', 'MatrixIndicesMap'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap'); |
| branch = branch(end); |
| tree = attributes(tree, 'add', branch, 'AppliesToMatrixDimension', printwithcomma(find(strcmp(dimtok, 'scalar'))-1)); |
| tree = attributes(tree, 'add', branch, 'IndicesMapToDataType', 'CIFTI_INDEX_TYPE_SCALARS'); |
| tree = add(tree, branch, 'element', 'NamedMap'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap/NamedMap'); |
| branch = branch(end); |
| [tree, uid] = add(tree, branch, 'element', 'MapName'); |
| tree = add(tree, uid, 'chardata', parameter); |
| end |
|
|
| if any(strcmp(dimtok, 'pos')) |
| |
| tree = add(tree, find(tree, 'CIFTI/Matrix'), 'element', 'MatrixIndicesMap'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap'); |
| branch = branch(end); |
| tree = attributes(tree, 'add', branch, 'AppliesToMatrixDimension', printwithcomma(find(strcmp(dimtok, 'pos'))-1)); |
| tree = attributes(tree, 'add', branch, 'IndicesMapToDataType', 'CIFTI_INDEX_TYPE_BRAIN_MODELS'); |
| |
| if isfield(source, 'dim') |
| switch source.unit |
| case 'mm' |
| MeterExponent = -3; |
| case 'cm' |
| MeterExponent = -2; |
| case 'dm' |
| MeterExponent = -1; |
| case 'm' |
| MeterExponent = 0; |
| otherwise |
| error('unsupported source.unit') |
| end |
| |
| [tree, uid] = add(tree, branch, 'element', 'Volume'); |
| tree = attributes(tree, 'add', uid, 'VolumeDimensions', printwithcomma(source.dim)); |
| [tree, uid] = add(tree, uid, 'element', 'TransformationMatrixVoxelIndicesIJKtoXYZ'); |
| tree = attributes(tree, 'add', uid, 'MeterExponent', num2str(MeterExponent)); |
| tree = add(tree, uid, 'chardata', printwithspace(source.transform')); % it needs to be transposed |
| |
| end |
| |
| for i=1:length(BrainStructurelabel) |
| % write one brainstructure for each group of vertices |
| if isempty(regexp(BrainStructurelabel{i}, '^CIFTI_STRUCTURE_', 'once')) |
| BrainStructurelabel{i} = ['CIFTI_STRUCTURE_' BrainStructurelabel{i}]; |
| end |
| |
| sel = (BrainStructure==i); |
| IndexCount = sum(sel); |
| IndexOffset = find(sel, 1, 'first') - 1; % zero offset |
| |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap'); |
| branch = branch(end); |
| tree = add(tree, branch, 'element', 'BrainModel'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap/BrainModel'); |
| branch = branch(end); |
| |
| if all(ModelType(sel)==find(strcmp(ModelTypelabel, 'VOXEL'))) |
| tree = attributes(tree, 'add', branch, 'IndexOffset', printwithspace(IndexOffset)); |
| tree = attributes(tree, 'add', branch, 'IndexCount', printwithspace(IndexCount)); |
| tree = attributes(tree, 'add', branch, 'ModelType', 'CIFTI_MODEL_TYPE_VOXELS'); |
| tree = attributes(tree, 'add', branch, 'BrainStructure', BrainStructurelabel{i}); |
| tree = add(tree, branch, 'element', 'VoxelIndicesIJK'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap/BrainModel/VoxelIndicesIJK'); |
| branch = branch(end); |
| tmp = source.pos(sel,:); |
| tmp = ft_warp_apply(inv(source.transform), tmp); |
| tmp = round(tmp)' - 1; |
| tree = add(tree, branch, 'chardata', printwithspace(tmp)); |
| else |
| tmp = find(sel)-IndexOffset-1; |
| tree = attributes(tree, 'add', branch, 'IndexOffset', printwithspace(IndexOffset)); |
| tree = attributes(tree, 'add', branch, 'IndexCount', printwithspace(IndexCount)); |
| tree = attributes(tree, 'add', branch, 'ModelType', 'CIFTI_MODEL_TYPE_SURFACE'); |
| tree = attributes(tree, 'add', branch, 'BrainStructure', BrainStructurelabel{i}); |
| tree = attributes(tree, 'add', branch, 'SurfaceNumberOfVertices', printwithspace(IndexCount)); |
| tree = add(tree, branch, 'element', 'VertexIndices'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap/BrainModel/VertexIndices'); |
| branch = branch(end); |
| tree = add(tree, branch, 'chardata', printwithspace(tmp)); |
| end |
| |
| end |
| |
| end |
|
|
| if any(strcmp(dimtok, 'chan')) |
| tree = add(tree, find(tree, 'CIFTI/Matrix'), 'element', 'MatrixIndicesMap'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap'); |
| branch = branch(end); |
| tree = attributes(tree, 'add', branch, 'AppliesToMatrixDimension', printwithcomma(find(strcmp(dimtok, 'chan'))-1)); |
| tree = attributes(tree, 'add', branch, 'IndicesMapToDataType', 'CIFTI_INDEX_TYPE_PARCELS'); |
| |
| if isfield(source, 'dim') |
| switch source.unit |
| case 'mm' |
| MeterExponent = -3; |
| case 'cm' |
| MeterExponent = -2; |
| case 'dm' |
| MeterExponent = -1; |
| case 'm' |
| MeterExponent = 0; |
| otherwise |
| error('unsupported source.unit') |
| end |
| |
| [tree, uid] = add(tree, branch, 'element', 'Volume'); |
| tree = attributes(tree, 'add', uid, 'VolumeDimensions', printwithcomma(source.dim)); |
| [tree, uid] = add(tree, uid, 'element', 'TransformationMatrixVoxelIndicesIJKtoXYZ'); |
| tree = attributes(tree, 'add', uid, 'MeterExponent', num2str(MeterExponent)); |
| tree = add(tree, uid, 'chardata', printwithspace(source.transform')); % it needs to be transposed |
| end |
| |
| % surfaces are described with vertex positions (pos/pnt) and triangles (tri) |
| if isfield(source, 'pos') && isfield(source, 'tri') |
| % there is a surface description |
| |
| for i=1:length(BrainStructurelabel) |
| sel = find(BrainStructure~=i); |
| [mesh.pnt, mesh.tri] = remove_vertices(source.pos, source.tri, sel); |
| mesh.unit = source.unit; |
| |
| if isempty(mesh.pnt) || isempty(mesh.tri) |
| % the brainordinate positions in this brain structure are not connected with triangles, i.e. in the case of voxels |
| continue; |
| end |
| |
| [tree, uid] = add(tree, branch, 'element', 'Surface'); |
| tree = attributes(tree, 'add', uid, 'BrainStructure', BrainStructurelabel{i}); |
| tree = attributes(tree, 'add', uid, 'SurfaceNumberOfVertices', printwithspace(size(mesh.pnt,1))); |
| end |
| |
| end % if tri |
| |
| parcel = source.label; % channels are used to represent parcels |
| for i=1:numel(parcel) |
| indx = find(strcmp(Parcellationlabel, parcel{i})); |
| if isempty(indx) |
| continue |
| end |
| selParcel = (Parcellation==indx); |
| structure = BrainStructurelabel(unique(BrainStructure(selParcel))); |
| |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap'); |
| branch = branch(end); |
| |
| tree = add(tree, branch, 'element', 'Parcel'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap/Parcel'); |
| branch = branch(end); |
| tree = attributes(tree, 'add', branch, 'Name', parcel{i}); |
| |
| % this is for pretty printing |
| maxparcellen = max(cellfun(@length, parcel)); |
| |
| for j=1:length(structure) |
| selStructure = (BrainStructure==find(strcmp(BrainStructurelabel, structure{j}))); |
| indx = find(selParcel & selStructure); |
| offset = find(selStructure, 1, 'first') - 1; |
| |
| fprintf('parcel |
| |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap/Parcel'); |
| branch = branch(end); |
| |
| if all(ModelType(selStructure)==find(strcmp(ModelTypelabel, 'VOXEL'))) |
| tree = add(tree, branch, 'element', 'VoxelIndicesIJK'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap/Parcel/VoxelIndicesIJK'); |
| branch = branch(end); |
| tmp = ft_warp_apply(inv(source.transform), source.pos(indx,:)); |
| tmp = round(tmp)' - 1; % transpose, zero offset |
| tree = add(tree, branch, 'chardata', printwithspace(tmp)); |
| else |
| tree = add(tree, branch, 'element', 'Vertices'); |
| branch = find(tree, 'CIFTI/Matrix/MatrixIndicesMap/Parcel/Vertices'); |
| branch = branch(end); |
| tree = attributes(tree, 'add', branch, 'BrainStructure', structure{j}); |
| tmp = indx - offset - 1; |
| tree = add(tree, branch, 'chardata', printwithspace(tmp)); |
| end |
| |
| end % for each structure contained in this parcel |
| end % for each parcel |
| end % if chan |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % write everything to file |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| |
| % 4 bytes with the size of the header, 384 for nifti-1 or 540 for nifti-2 |
| % 540 bytes with the nifti-2 header |
| % 4 bytes that indicate the presence of a header extension [1 0 0 0] |
| % 4 bytes with the size of the header extension in big endian? |
| % 4 bytes with the header extension code NIFTI_ECODE_CIFTI [0 0 0 32] |
| % variable number of bytes with the xml section, at the end there might be some empty "junk" |
| % 8 bytes, presumaby with the size and type? |
| % variable number of bytes with the voxel data |
| |
| xmlfile = [tempname '.xml']; % this will contain the cifti XML structure |
| save(tree, xmlfile); % write the XMLTREE object to disk |
| |
| xmlfid = fopen(xmlfile, 'rb'); |
| xmldat = fread(xmlfid, [1, inf], 'char'); |
| fclose(xmlfid); |
| |
| % we do not want the content of the XML elements to be nicely formatted, as it might create confusion when reading |
| % therefore detect and remove whitespace immediately following a ">" and preceding a "<" |
| whitespace = false(size(xmldat)); |
| |
| gt = int8('>'); |
| lt = int8('<'); |
| ws = int8(sprintf(' \t\r\n')); |
| |
| b = find(xmldat==gt); |
| e = find(xmldat==lt); |
| b = b(1:end-1); % the XML section ends with ">", this is not of relevance |
| e = e(2:end); % the XML section starts with "<", this is not of relevance |
| b = b+1; |
| e = e-1; |
| |
| for i=1:length(b) |
| for j=b(i):1:e(i) |
| if any(ws==xmldat(j)) |
| whitespace(j) = true; |
| else |
| break |
| end |
| end |
| end |
| |
| for i=1:length(b) |
| for j=e(i):-1:b(i) |
| if any(ws==xmldat(j)) |
| whitespace(j) = true; |
| else |
| break |
| end |
| end |
| end |
| |
| % keep it if there is _only_ whitespace between the ">" and "<" |
| for i=1:length(b) |
| if all(whitespace(b(i):e(i))) |
| whitespace(b(i):e(i)) = false; |
| end |
| end |
| |
| % remove the padding whitespace |
| xmldat = xmldat(~whitespace); |
| |
| % the header extension needs to be aligned |
| xmlsize = length(xmldat); |
| xmlpad = ceil((xmlsize+8)/16)*16 - (xmlsize+8); |
| |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % construct the NIFTI-2 header |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| |
| hdr.magic = [110 43 50 0 13 10 26 10]; |
| |
| % see http://nifti.nimh.nih.gov/nifti-1/documentation/nifti1fields/nifti1fields_pages/datatype.html |
| switch precision |
| case 'uint8' |
| hdr.datatype = 2; |
| case 'int16' |
| hdr.datatype = 4; |
| case 'int32' |
| hdr.datatype = 8; |
| case 'single' |
| hdr.datatype = 16; |
| case 'double' |
| hdr.datatype = 64; |
| case 'int8' |
| hdr.datatype = 256; |
| case 'uint16' |
| hdr.datatype = 512; |
| case 'uint32' |
| hdr.datatype = 768; |
| case 'uint64' |
| hdr.datatype = 1280; |
| case 'int64' |
| hdr.datatype = 1024; |
| otherwise |
| error('unsupported precision "%s"', precision); |
| end |
| |
| switch precision |
| case {'uint8' 'int8'} |
| hdr.bitpix = 1*8; |
| case {'uint16' 'int16'} |
| hdr.bitpix = 2*8; |
| case {'uint32' 'int32'} |
| hdr.bitpix = 4*8; |
| case {'uint64' 'int64'} |
| hdr.bitpix = 8*8; |
| case 'single' |
| hdr.bitpix = 4*8; |
| case 'double' |
| hdr.bitpix = 8*8; |
| otherwise |
| error('unsupported precision "%s"', precision); |
| end |
| |
| % dim(1) represents the number of dimensions |
| % for a normal nifti file, dim(2:4) are x, y, z, dim(5) is time |
| % cifti makes use of dim(6:8), which are free to choose |
| hdr.dim = [4+length(dimtok) 1 1 1 1 1 1 1]; |
| |
| % the nifti specification does not allow for more than 7 dimensions to be specified |
| assert(hdr.dim(1)<8); |
| |
| for i=1:length(dimtok) |
| switch dimtok{i} |
| case 'pos' |
| hdr.dim(5+i) = size(source.pos,1); |
| case 'chan' |
| hdr.dim(5+i) = numel(source.label); |
| case 'time' |
| hdr.dim(5+i) = numel(source.time); |
| case 'freq' |
| hdr.dim(5+i) = numel(source.freq); |
| case 'scalar' |
| hdr.dim(5+i) = 1; |
| otherwise |
| error('unsupported dimord "%s"', dimord) |
| end |
| end |
| |
| hdr.intent_p1 = 0; |
| hdr.intent_p2 = 0; |
| hdr.intent_p3 = 0; |
| hdr.pixdim = [0 1 1 1 1 1 1 1]; |
| hdr.vox_offset = 4+540+8+xmlsize+xmlpad; |
| hdr.scl_slope = 1; % WorkBench sets scl_slope/scl_inter to 1 and 0, although 0 and 0 would also be fine - both mean the same thing according to the nifti spec |
| hdr.scl_inter = 0; |
| hdr.cal_max = 0; |
| hdr.cal_min = 0; |
| hdr.slice_duration = 0; |
| hdr.toffset = 0; |
| hdr.slice_start = 0; |
| hdr.slice_end = 0; |
| hdr.descrip = char(zeros(1,80)); |
| hdr.aux_file = char(zeros(1,24)); |
| hdr.qform_code = 0; |
| hdr.sform_code = 0; |
| hdr.quatern_b = 0; |
| hdr.quatern_c = 0; |
| hdr.quatern_d = 0; |
| hdr.qoffset_x = 0; |
| hdr.qoffset_y = 0; |
| hdr.qOffset_z = 0; |
| hdr.srow_x = [0 0 0 0]; |
| hdr.srow_y = [0 0 0 0]; |
| hdr.srow_z = [0 0 0 0]; |
| hdr.slice_code = 0; |
| hdr.xyzt_units = 0; |
| hdr.intent_code = intent_code; |
| hdr.intent_name = cat(2, intent_name, zeros(1, 16-length(intent_name))); % zero-pad up to 16 characters |
| hdr.dim_info = 0; |
| hdr.unused_str = char(zeros(1,15)); |
| |
| % open the file |
| fid = fopen(filename, 'wb'); |
| |
| % write the header, this is 4+540 bytes |
| write_nifti2_hdr(fid, hdr); |
| |
| if debug |
| try |
| % write the xml section to a temporary file for debugging |
| xmlfile = 'debug.xml'; |
| tmp = fopen(xmlfile, 'w'); |
| fwrite(tmp, xmldat, 'char'); |
| fclose(tmp); |
| end |
| end |
| |
| % write the cifti header extension |
| fwrite(fid, [1 0 0 0], 'uint8'); |
| fwrite(fid, 8+xmlsize+xmlpad, 'int32'); % esize |
| fwrite(fid, 32, 'int32'); % etype |
| fwrite(fid, xmldat, 'char'); % write the ascii XML section |
| fwrite(fid, zeros(1,xmlpad), 'uint8'); % zero-pad to the next 16 byte boundary |
| |
| % write the actual data |
| fwrite(fid, dat, precision); |
| |
| fclose(fid); |
| |
| % write the surfaces as gifti files |
| if writesurface && isfield(source, 'pos') && isfield(source, 'tri') |
| |
| if isfield(source, brainstructure) |
| % it contains information about anatomical structures, including cortical surfaces |
| for i=1:length(BrainStructurelabel) |
| sel = find(BrainStructure~=i); |
| [mesh.pnt, mesh.tri] = remove_vertices(source.pos, source.tri, sel); |
| mesh.unit = source.unit; |
| |
| if isempty(mesh.pnt) || isempty(mesh.tri) |
| % the brainordinate positions in this brain structure are not connected with triangles, i.e. in the case of voxels |
| continue; |
| end |
| |
| if ~isempty(regexp(BrainStructurelabel{i}, '^CIFTI_STRUCTURE_', 'once')) |
| BrainStructurelabel{i} = BrainStructurelabel{i}(17:end); |
| end |
| |
| [p, f, x] = fileparts(filename); |
| filetok = tokenize(f, '.'); |
| surffile = fullfile(p, [filetok{1} '.' BrainStructurelabel{i} '.surf.gii']); |
| fprintf('writing |
| ft_write_headshape(surffile, mesh, 'format', 'gifti'); |
| end |
| |
| else |
| mesh.pnt = source.pos; |
| mesh.tri = source.tri; |
| mesh.unit = source.unit; |
| |
| [p, f, x] = fileparts(filename); |
| filetok = tokenize(f, '.'); |
| surffile = fullfile(p, [filetok{1} '.surf.gii']); |
| ft_write_headshape(surffile, mesh, 'format', 'gifti'); |
| end |
| |
| end |
|
|
|
|
| |
| |
| |
|
|
| function s = printwithspace(x) |
| x = x(:)'; % convert to vector |
| if all(round(x)==x) |
| % print as integer value |
| s = sprintf(' |
| else |
| |
| s = sprintf('%f ', x); |
| end |
| s = s(1:end-1); |
|
|
| function s = printwithcomma(x) |
| x = x(:)'; % convert to vector |
| if all(round(x)==x) |
| % print as integer value |
| s = sprintf(' |
| else |
| |
| s = sprintf('%f,', x); |
| end |
| s = s(1:end-1); |
|
|
| function s = stringpad(s, n) |
| while length(s)<n |
| s = [' ' s]; |
| end |
|
|
| |
| |
| |
|
|
| function [pntR, triR] = remove_vertices(pnt, tri, removepnt) |
|
|
| npnt = size(pnt,1); |
| ntri = size(tri,1); |
|
|
| if all(removepnt==0 | removepnt==1) |
| removepnt = find(removepnt); |
| end |
|
|
| |
| keeppnt = setdiff(1:npnt, removepnt); |
| numb = zeros(1,npnt); |
| numb(keeppnt) = 1:length(keeppnt); |
|
|
| |
| removetri = false(ntri,1); |
| removetri(ismember(tri(:,1), removepnt)) = true; |
| removetri(ismember(tri(:,2), removepnt)) = true; |
| removetri(ismember(tri(:,3), removepnt)) = true; |
|
|
| |
| pntR = pnt(keeppnt, :); |
| triR = tri(~removetri,:); |
|
|
| |
| triR = numb(triR); |
|
|