0001 function [cube, metadata] = matRad_readNRRD(filename)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031 hFile = fopen(filename, 'r');
0032 if hFile <= 0
0033 error('Could not open NRRD file!');
0034 end
0035 cleaner = onCleanup(@() fclose(hFile));
0036
0037
0038 nrrdLine = fgetl(hFile);
0039 regTokens = regexp(nrrdLine,'NRRD00(?:\.)?0([1-5])','tokens');
0040 if isempty(regTokens)
0041 error('Invalid Header line! Could not identify NRRD version!');
0042 end
0043 nrrdVersion = str2num(regTokens{1}{1});
0044
0045 if nrrdVersion > 5
0046 error('NRRD version > 5 not supported!');
0047 end
0048
0049
0050 nrrdMetaData.fields = cell(0,2);
0051 nrrdMetaData.keys = cell(0,2);
0052 nrrdMetaData.comments = cell(0);
0053
0054 currentLine = fgetl(hFile);
0055 while ~isempty(currentLine) && ischar(currentLine)
0056
0057 if isequal(currentLine(1),'#')
0058 nrrdMetaData.comments{end+1} = currentLine;
0059 else
0060
0061 lineContent = regexp(currentLine, '(.+):(=|\s)(.+)', 'tokens');
0062 if isempty(lineContent)
0063 warning(['Could not parse line: "' lineContent '"']);
0064 elseif isequal(lineContent{1}{2},' ')
0065 nrrdMetaData.fields{end+1,1} = lineContent{1}{1};
0066 nrrdMetaData.fields{end,2} = lineContent{1}{3};
0067 elseif isequal(lineContent{1}{2},'=')
0068 nrrdMetaData.keys{end+1,1} = lineContent{1}{1};
0069 nrrdMetaData.keys{end,2} = lineContent{1}{3};
0070 else
0071 warning(['Could not parse line: "' lineContent '"']);
0072 end
0073 end
0074 currentLine = fgetl(hFile);
0075 end
0076
0077
0078
0079 doSwapBytes = false;
0080 typeFieldIx = find(ismember(nrrdMetaData.fields(:,1), 'type'));
0081 if ~isempty(typeFieldIx)
0082 datatype = getMATLABdataType(nrrdMetaData.fields{typeFieldIx,2});
0083
0084 if ~isequal(datatype(end),'8')
0085 endianFieldIx = find(ismember(nrrdMetaData.fields(:,1),'endian'));
0086 if ~isempty(endianFieldIx)
0087 if ~isequal(nrrdMetaData.fields{endianFieldIx,2},'little') && ~isequal(nrrdMetaData.fields{endianFieldIx,2},'big')
0088 error(['Datatype is ' datatype ', thus endian information is required but could not be interpreted!']);
0089 end;
0090
0091
0092 [~,~,endian] = computer();
0093 if isequal(endian,'B')
0094 endian = 'big';
0095 end;
0096 if isequal(endian,'L')
0097 endian = 'little';
0098 end;
0099
0100 if ~isequal(endian,nrrdMetaData.fields{endianFieldIx,2})
0101 doSwapBytes = true;
0102 end
0103 else
0104 error(['Datatype is ' datatype ', thus endian information is required but could not be found!']);
0105 end
0106 end
0107 metadata.datatype = datatype;
0108
0109 else
0110 error('Could not find required "type" field!');
0111 end
0112
0113
0114 dimFieldIx = find(ismember(nrrdMetaData.fields(:,1), 'dimension'));
0115 if ~isempty(dimFieldIx)
0116 [metadata.dimension,success] = str2num(nrrdMetaData.fields{dimFieldIx,2});
0117 if ~success
0118 error('Could not read required dimension field');
0119 end
0120 else
0121 error('Could not find required "dimension" field!');
0122 end
0123
0124
0125 sizeFieldIx = find(ismember(nrrdMetaData.fields(:,1), 'sizes'));
0126 if ~isempty(sizeFieldIx)
0127 sizes = textscan(nrrdMetaData.fields{sizeFieldIx,2},'%d');
0128 if numel(sizes{1}) ~= metadata.dimension || ~all(sizes{1} > 0)
0129 error('Incorrect size definition!');
0130 end
0131 metadata.cubeDim = sizes{1}';
0132 else
0133 error('Could not find required "dimension" field!');
0134 end
0135
0136
0137
0138 spacingFieldIx = find(ismember(nrrdMetaData.fields(:,1), 'spacings'));
0139 spaceDirFieldIx = find(ismember(nrrdMetaData.fields(:,1), 'space directions'));
0140 if ~isempty(spacingFieldIx)
0141 resolutions = textscan(nrrdMetaData.fields{spacingFieldIx,2},'%f');
0142 if numel(resolutions{1}) ~= metadata.dimension
0143 error('Incorrect spacings definition');
0144 end
0145 metadata.resolution = resolutions{1}';
0146
0147
0148
0149 metadata.axisPermutation = [1 2 3];
0150
0151 elseif ~isempty(spaceDirFieldIx)
0152
0153
0154 vectorstring = '(';
0155 for c=1:metadata.dimension
0156 vectorstring = [vectorstring '%f,'];
0157 end
0158 vectorstring(end) = ')';
0159
0160 vectors = textscan(nrrdMetaData.fields{spaceDirFieldIx,2},vectorstring);
0161
0162
0163
0164
0165 for c=1:metadata.dimension
0166
0167 currentAxis = find(vectors{c});
0168
0169 if numel(find(vectors{c})) ~= 1
0170 error('Sorry! We currently only support spaces with cartesian basis!');
0171 end
0172 metadata.axisPermutation(c) = currentAxis*sign(vectors{c}(currentAxis));
0173 metadata.resolution(c) = vectors{c}(currentAxis);
0174 end
0175
0176 else
0177 warning('No Resolution Information available');
0178 end
0179
0180
0181 originFieldIx = find(ismember(nrrdMetaData.fields(:,1), 'space origin'));
0182 if ~isempty(originFieldIx)
0183
0184 vectorstring = '(';
0185 for c=1:metadata.dimension
0186 vectorstring = [vectorstring '%f,'];
0187 end
0188 originVector = textscan(nrrdMetaData.fields{originFieldIx,2},vectorstring);
0189 for c=1:metadata.dimension
0190 metadata.imageOrigin(c) = originVector{c};
0191 end
0192
0193 end
0194
0195
0196 originFieldIx = find(ismember(nrrdMetaData.fields(:,1), 'space'));
0197 if ~isempty(originFieldIx)
0198 metadata.coordinateSystem = nrrdMetaData.fields{originFieldIx,2};
0199 end
0200
0201
0202 data_fileFieldIx = find(ismember(nrrdMetaData.fields(:,1), 'data file'));
0203 datafileFieldIx = find(ismember(nrrdMetaData.fields(:,1), 'datafile'));
0204 if ~isempty(data_fileFieldIx) || ~isempty(datafileFieldIx)
0205 error('Sorry! We currently do not support detached data files!');
0206
0207
0208
0209
0210
0211 end
0212
0213
0214
0215
0216 encodingFieldIx = find(ismember(nrrdMetaData.fields(:,1), 'encoding'));
0217 if isempty(encodingFieldIx)
0218 error('Could not find required "encoding" field!');
0219 end
0220 switch nrrdMetaData.fields{encodingFieldIx,2}
0221 case 'raw'
0222 cube = fread(hFile,prod(metadata.cubeDim), metadata.datatype);
0223 case {'txt','text','ascii'}
0224 cube = cast(fscanf(hFile,'%f'),metadata.datatype);
0225 case 'hex'
0226 error('Sorry: NRRD hex file not yet supported!');
0227 case {'gz','gzip'}
0228 compressedByteArray = fread(hFile, inf, 'uint8');
0229
0230 javaUnpackSuccessful = false;
0231
0232 if isempty(javachk('jvm'))
0233
0234 try
0235 javaByteStream = java.io.ByteArrayInputStream(compressedByteArray);
0236 gzipStream = java.util.zip.GZIPInputStream(javaByteStream);
0237 javaByteOutputStream = java.io.ByteArrayOutputStream();
0238 org.apache.commons.io.IOUtils.copy(gzipStream,javaByteOutputStream);
0239 gzipStream.close();
0240 cube = typecast(javaByteOutputStream.toByteArray(),metadata.datatype)';
0241 javaByteStream.close();
0242 javaByteOutputStream.close();
0243 javaUnpackSuccessful = true;
0244 catch
0245 warning('Java unpacking failed... using temporary files!');
0246 end
0247 end
0248 if ~javaUnpackSuccessful
0249
0250 tmpName = tempname();
0251 tmpFile = [tmpName '.gz'];
0252 hFileTmp = fopen(tmpFile, 'wb');
0253
0254 if hFileTmp <= 0
0255 error('Could not open temporary file for GZIP!');
0256 end
0257
0258 fwrite(hFileTmp, compressedByteArray, 'uint8');
0259 fclose(hFileTmp);
0260
0261
0262 gunzip(tmpFile);
0263
0264
0265 hFileTmp = fopen(tmpName, 'rb');
0266 if hFileTmp <= 0
0267 error('Could not open unpacked file!');
0268 end
0269 cleanTmpFile = onCleanup(@() fclose(hFileTmp));
0270
0271 cube = fread(hFileTmp, prod(metadata.cubeDim), metadata.datatype);
0272 end
0273 case {'bz2','bzip2'}
0274 error('Sorry: bzip compression not yet supported!');
0275 otherwise
0276 error(['Undefined NRRD encoding scheme: ' nrrdMetaData.encoding]);
0277 end
0278
0279
0280 if doSwapBytes
0281 swapbytes(cube);
0282 end
0283
0284 if numel(metadata.cubeDim) > 1
0285
0286
0287 cube = reshape(cube,metadata.cubeDim);
0288
0289
0290
0291
0292 permutationTransformMatrix = diag(ones(metadata.dimension,1));
0293 permutationTransformMatrix(1:2,1:2) = flip(permutationTransformMatrix(1:2,1:2));
0294
0295 applyPermutation = permutationTransformMatrix*metadata.axisPermutation';
0296
0297 cube = permute(cube,applyPermutation);
0298 end
0299
0300
0301
0302 end
0303
0304
0305 function datatype = getMATLABdataType(typestring)
0306
0307
0308 switch typestring
0309 case {'signed char','int8','int8_t'}
0310 datatype = 'int8';
0311 case {'uchar','unsigned char','uint8','uint8_t'}
0312 datatype = 'uint8';
0313 case {'short','short int','signed short','signed short int','int16','int16_t'}
0314 datatype = 'int16';
0315 case {'ushort','unsigned short','unsigned short int','uint16','uint16_t'}
0316 datatype = 'uint16';
0317 case {'int','signed int','int32','int32_t'}
0318 datatype = 'int32';
0319 case {'uint','unsigned int','uint32','uint32_t'}
0320 datatype = 'uint32';
0321 case {'longlong','long long','long long int','signed long long','signed long long int','int64','int64_t'}
0322 datatype = 'int64';
0323 case {'ulonglong', 'unsigned long long', 'unsigned long long int','uint64','uint64_t'}
0324 datatype = 'uint64';
0325 case 'float'
0326 datatype = 'single';
0327 case 'double'
0328 datatype = 'double';
0329 otherwise
0330 error('Could not identify datatype for NRRD data');
0331 end
0332
0333 end
0334