66import warnings
77import zlib
88from collections import OrderedDict
9+ from typing import IO , AnyStr , Iterable , Tuple
910
1011from nrrd .parsers import *
12+ from nrrd .types import IndexOrder , NRRDFieldMap , NRRDFieldType , NRRDHeader
1113
1214# Older versions of Python had issues when uncompressed data was larger than 4GB (2^32). This should be fixed in latest
1315# version of Python 2.7 and all versions of Python 3. The fix for this issue is to read the data in smaller chunks.
1416# Chunk size is set to be large at 1GB to improve performance. If issues arise decompressing larger files, try to reduce
1517# this value
16- _READ_CHUNKSIZE = 2 ** 32
18+ _READ_CHUNKSIZE : int = 2 ** 32
1719
1820_NRRD_REQUIRED_FIELDS = ['dimension' , 'type' , 'encoding' , 'sizes' ]
1921
8486}
8587
8688
87- def _get_field_type (field , custom_field_map ) :
89+ def _get_field_type (field : str , custom_field_map : Optional [ NRRDFieldMap ]) -> NRRDFieldType :
8890 if field in ['dimension' , 'lineskip' , 'line skip' , 'byteskip' , 'byte skip' , 'space dimension' ]:
8991 return 'int'
9092 elif field in ['min' , 'max' , 'oldmin' , 'old min' , 'oldmax' , 'old max' ]:
@@ -99,7 +101,7 @@ def _get_field_type(field, custom_field_map):
99101 return 'string list'
100102 elif field in ['labels' , 'units' , 'space units' ]:
101103 return 'quoted string list'
102- # No int vector fields as of now
104+ # No int vector fields yet
103105 # elif field in []:
104106 # return 'int vector'
105107 elif field in ['space origin' ]:
@@ -116,7 +118,7 @@ def _get_field_type(field, custom_field_map):
116118 return 'string'
117119
118120
119- def _parse_field_value (value , field_type ) :
121+ def _parse_field_value (value : str , field_type : NRRDFieldType ) -> Any :
120122 if field_type == 'int' :
121123 return int (value )
122124 elif field_type == 'double' :
@@ -146,28 +148,28 @@ def _parse_field_value(value, field_type):
146148 raise NRRDError (f'Invalid field type given: { field_type } ' )
147149
148150
149- def _determine_datatype (fields ) :
151+ def _determine_datatype (header : NRRDHeader ) -> np . dtype :
150152 """Determine the numpy dtype of the data."""
151153
152154 # Convert the NRRD type string identifier into a NumPy string identifier using a map
153- np_typestring = _TYPEMAP_NRRD2NUMPY [fields ['type' ]]
155+ np_typestring = _TYPEMAP_NRRD2NUMPY [header ['type' ]]
154156
155157 # This is only added if the datatype has more than one byte and is not using ASCII encoding
156158 # Note: Endian is not required for ASCII encoding
157- if np .dtype (np_typestring ).itemsize > 1 and fields ['encoding' ] not in ['ASCII' , 'ascii' , 'text' , 'txt' ]:
158- if 'endian' not in fields :
159+ if np .dtype (np_typestring ).itemsize > 1 and header ['encoding' ] not in ['ASCII' , 'ascii' , 'text' , 'txt' ]:
160+ if 'endian' not in header :
159161 raise NRRDError ('Header is missing required field: endian' )
160- elif fields ['endian' ] == 'big' :
162+ elif header ['endian' ] == 'big' :
161163 np_typestring = '>' + np_typestring
162- elif fields ['endian' ] == 'little' :
164+ elif header ['endian' ] == 'little' :
163165 np_typestring = '<' + np_typestring
164166 else :
165- raise NRRDError (f'Invalid endian value in header: { fields ["endian" ]} ' )
167+ raise NRRDError (f'Invalid endian value in header: { header ["endian" ]} ' )
166168
167169 return np .dtype (np_typestring )
168170
169171
170- def _validate_magic_line (line ) :
172+ def _validate_magic_line (line : str ) -> int :
171173 """For NRRD files, the first four characters are always "NRRD", and
172174 remaining characters give information about the file format version
173175
@@ -197,7 +199,7 @@ def _validate_magic_line(line):
197199 return len (line )
198200
199201
200- def read_header (file , custom_field_map = None ):
202+ def read_header (file : Union [ str , Iterable [ AnyStr ]], custom_field_map : Optional [ NRRDFieldMap ] = None ) -> NRRDHeader :
201203 """Read contents of header and parse values from :obj:`file`
202204
203205 :obj:`file` can be a filename indicating where the NRRD header is located or a string iterator object. If a
@@ -284,7 +286,7 @@ def read_header(file, custom_field_map=None):
284286 else :
285287 warnings .warn (f'Duplicate header field: { field } ' )
286288
287- # Get the datatype of the field based on it's field name and custom field map
289+ # Get the datatype of the field based on its field name and custom field map
288290 field_type = _get_field_type (field , custom_field_map )
289291
290292 # Parse the field value using the datatype retrieved
@@ -299,7 +301,8 @@ def read_header(file, custom_field_map=None):
299301 return header
300302
301303
302- def read_data (header , fh = None , filename = None , index_order = 'F' ):
304+ def read_data (header : NRRDHeader , fh : Optional [IO ] = None , filename : Optional [str ] = None ,
305+ index_order : IndexOrder = 'F' ) -> npt .NDArray :
303306 """Read data from file into :class:`numpy.ndarray`
304307
305308 The two parameters :obj:`fh` and :obj:`filename` are optional depending on the parameters but it never hurts to
@@ -427,7 +430,7 @@ def read_data(header, fh=None, filename=None, index_order='F'):
427430 # Loop through the file and read a chunk at a time (see _READ_CHUNKSIZE why it is read in chunks)
428431 decompressed_data = bytearray ()
429432
430- # Read all of the remaining data from the file
433+ # Read all the remaining data from the file
431434 # Obtain the length of the compressed data since we will be using it repeatedly, more efficient
432435 compressed_data = fh .read ()
433436 compressed_data_len = len (compressed_data )
@@ -474,7 +477,8 @@ def read_data(header, fh=None, filename=None, index_order='F'):
474477 return data
475478
476479
477- def read (filename , custom_field_map = None , index_order = 'F' ):
480+ def read (filename : str , custom_field_map : Optional [NRRDFieldMap ] = None , index_order : IndexOrder = 'F' ) \
481+ -> Tuple [npt .NDArray , NRRDHeader ]:
478482 """Read a NRRD file and return the header and data
479483
480484 See :ref:`user-guide:Reading NRRD files` for more information on reading NRRD files.
0 commit comments