nrrdread.m 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. function [X, meta] = nrrdread(filename)
  2. %NRRDREAD Import NRRD imagery and metadata.
  3. % [X, META] = NRRDREAD(FILENAME) reads the image volume and associated
  4. % metadata from the NRRD-format file specified by FILENAME.
  5. %
  6. % Current limitations/caveats:
  7. % * "Block" datatype is not supported.
  8. % * Only tested with "gzip" and "raw" file encodings.
  9. % * Very limited testing on actual files.
  10. % * I only spent a couple minutes reading the NRRD spec.
  11. %
  12. % See the format specification online:
  13. % http://teem.sourceforge.net/nrrd/format.html
  14. % Copyright 2012 The MathWorks, Inc.
  15. % Open file.
  16. fid = fopen(filename, 'rb');
  17. assert(fid > 0, 'Could not open file.');
  18. cleaner = onCleanup(@() fclose(fid));
  19. % Magic line.
  20. theLine = fgetl(fid);
  21. assert(numel(theLine) >= 4, 'Bad signature in file.')
  22. assert(isequal(theLine(1:4), 'NRRD'), 'Bad signature in file.')
  23. % The general format of a NRRD file (with attached header) is:
  24. %
  25. % NRRD000X
  26. % <field>: <desc>
  27. % <field>: <desc>
  28. % # <comment>
  29. % ...
  30. % <field>: <desc>
  31. % <key>:=<value>
  32. % <key>:=<value>
  33. % <key>:=<value>
  34. % # <comment>
  35. %
  36. % <data><data><data><data><data><data>...
  37. meta = struct([]);
  38. % Parse the file a line at a time.
  39. while (true)
  40. theLine = fgetl(fid);
  41. if (isempty(theLine) || feof(fid))
  42. % End of the header.
  43. break;
  44. end
  45. if (isequal(theLine(1), '#'))
  46. % Comment line.
  47. continue;
  48. end
  49. % "fieldname:= value" or "fieldname: value" or "fieldname:value"
  50. parsedLine = regexp(theLine, ':=?\s*', 'split','once');
  51. assert(numel(parsedLine) == 2, 'Parsing error')
  52. field = lower(parsedLine{1});
  53. value = parsedLine{2};
  54. field(isspace(field)) = '';
  55. meta(1).(field) = value;
  56. end
  57. datatype = getDatatype(meta.type);
  58. % Get the size of the data.
  59. assert(isfield(meta, 'sizes') && ...
  60. isfield(meta, 'dimension') && ...
  61. isfield(meta, 'encoding') && ...
  62. isfield(meta, 'endian'), ...
  63. 'Missing required metadata fields.')
  64. dims = sscanf(meta.sizes, '%d');
  65. ndims = sscanf(meta.dimension, '%d');
  66. assert(numel(dims) == ndims);
  67. data = readData(fid, meta, datatype);
  68. data = adjustEndian(data, meta);
  69. % Reshape and get into MATLAB's order.
  70. X = reshape(data, dims');
  71. X = permute(X, [2 1 3]);
  72. function datatype = getDatatype(metaType)
  73. % Determine the datatype
  74. switch (metaType)
  75. case {'signed char', 'int8', 'int8_t'}
  76. datatype = 'int8';
  77. case {'uchar', 'unsigned char', 'uint8', 'uint8_t'}
  78. datatype = 'uint8';
  79. case {'short', 'short int', 'signed short', 'signed short int', ...
  80. 'int16', 'int16_t'}
  81. datatype = 'int16';
  82. case {'ushort', 'unsigned short', 'unsigned short int', 'uint16', ...
  83. 'uint16_t'}
  84. datatype = 'uint16';
  85. case {'int', 'signed int', 'int32', 'int32_t'}
  86. datatype = 'int32';
  87. case {'uint', 'unsigned int', 'uint32', 'uint32_t'}
  88. datatype = 'uint32';
  89. case {'longlong', 'long long', 'long long int', 'signed long long', ...
  90. 'signed long long int', 'int64', 'int64_t'}
  91. datatype = 'int64';
  92. case {'ulonglong', 'unsigned long long', 'unsigned long long int', ...
  93. 'uint64', 'uint64_t'}
  94. datatype = 'uint64';
  95. case {'float'}
  96. datatype = 'single';
  97. case {'double'}
  98. datatype = 'double';
  99. otherwise
  100. assert(false, 'Unknown datatype')
  101. end
  102. function data = readData(fidIn, meta, datatype)
  103. switch (meta.encoding)
  104. case {'raw'}
  105. data = fread(fidIn, inf, [datatype '=>' datatype]);
  106. case {'gzip', 'gz'}
  107. tmpBase = tempname();
  108. tmpFile = [tmpBase '.gz'];
  109. fidTmp = fopen(tmpFile, 'wb');
  110. assert(fidTmp > 3, 'Could not open temporary file for GZIP decompression')
  111. tmp = fread(fidIn, inf, 'uint8=>uint8');
  112. fwrite(fidTmp, tmp, 'uint8');
  113. fclose(fidTmp);
  114. gunzip(tmpFile)
  115. fidTmp = fopen(tmpBase, 'rb');
  116. cleaner = onCleanup(@() fclose(fidTmp));
  117. meta.encoding = 'raw';
  118. data = readData(fidTmp, meta, datatype);
  119. case {'txt', 'text', 'ascii'}
  120. data = fscanf(fidIn, '%f');
  121. data = cast(data, datatype);
  122. otherwise
  123. assert(false, 'Unsupported encoding')
  124. end
  125. function data = adjustEndian(data, meta)
  126. [~,~,endian] = computer();
  127. needToSwap = (isequal(endian, 'B') && isequal(lower(meta.endian), 'little')) || ...
  128. (isequal(endian, 'L') && isequal(lower(meta.endian), 'big'));
  129. if (needToSwap)
  130. data = swapbytes(data);
  131. end