# NiBabel

### Access a cacophony of neuro-imaging file formats

# `volumeutils`

Utility functions for analyze-like formats

`DtypeMapper`() | Specialized mapper for numpy dtypes
`Recoder`(codes, fields, map_maker, ...) | class to return canonical code(s) from code or aliases
`apply_read_scaling`(arr[, slope, inter]) | Apply scaling in slope and inter to array arr
`array_from_file`(shape, in_dtype, infile[, ...]) | Get array from file with specified shape, dtype and file offset
`array_to_file`(data, fileobj[, out_dtype, ...]) | Helper function for writing arrays to file objects
`best_write_scale_ftype`(arr, slope, inter, ...) | Smallest float type to contain range of `arr` after scaling
`better_float_of`(first, second, default) | Return more capable float type of first and second
`finite_range`(-> tuple[Scalar, Scalar]) | Get range (min, max) or range and flag (min, max, has_nan) from arr
`fname_ext_ul_case`(fname) | fname with ext changed to upper / lower case if file exists
`int_scinter_ftype`(ifmt, slope, inter, default) | float type containing int type ifmt * slope + inter
`make_dt_codes`(codes_seqs) | Create full dt codes Recoder instance from datatype codes
`pretty_mapping`(mapping[, getterfunc]) | Make pretty string from mapping
`rec2dict`(rec) | Convert recarray to dictionary
`seek_tell`(fileobj, offset[, write0]) | Seek in fileobj or check we're in the right place already
`shape_zoom_affine`(shape, zooms[, x_flip]) | Get affine implied by given shape and zooms
`working_type`(in_type[, slope, inter]) | Return array type from applying slope, inter to array of in_type
`write_zeros`(fileobj, count[, block_size]) | Write count zero bytes to fileobj

## `DtypeMapper`

class nibabel.volumeutils.DtypeMapper

  Bases: `dict`[`Hashable`, `Hashable`]

  Specialized mapper for numpy dtypes

  We pass this mapper into the Recoder class to deal with numpy dtype hashing.

  The hashing problem is that dtypes that compare equal may not have the same hash. This is true for numpys up to the current at time of writing (1.6.0). For numpy 1.2.1 at least, even dtypes that look exactly the same in terms of fields don’t always have the same hash. This makes dtypes difficult to use as keys in a dictionary.

  This class wraps a dictionary in order to implement a __getitem__ to deal with dtype hashing. If the key doesn’t appear to be in the mapping, and it is a dtype, we compare (using ==) all known dtype keys to the input key, and return any matching values for the matching key.

  __init__() → None

## `Recoder`

class nibabel.volumeutils.Recoder(codes: ~typing.Sequence[~typing.Sequence[~typing.Hashable]], fields: ~typing.Sequence[str] = ('code',), map_maker: type[~typing.Mapping[~typing.Hashable, ~typing.Hashable]] = <class 'dict'>)

  Bases: `object`

  class to return canonical code(s) from code or aliases

  The concept is a lot easier to read in the implementation and tests than it is to explain, so…

      >>> # If you have some codes, and several aliases, like this:
      >>> code1 = 1; aliases1=['one', 'first']
      >>> code2 = 2; aliases2=['two', 'second']
      >>> # You might want to do this:
      >>> codes = [[code1]+aliases1,[code2]+aliases2]
      >>> recodes = Recoder(codes)
      >>> recodes.code['one']
      1
      >>> recodes.code['second']
      2
      >>> recodes.code[2]
      2
      >>> # Or maybe you have a code, a label and some aliases
      >>> codes=((1,'label1','one', 'first'),(2,'label2','two'))
      >>> # you might want to get back the code or the label
      >>> recodes = Recoder(codes, fields=('code','label'))
      >>> recodes.code['first']
      1
      >>> recodes.code['label1']
      1
      >>> recodes.label[2]
      'label2'
      >>> # For convenience, you can get the first entered name by
      >>> # indexing the object directly
      >>> recodes[2]
      2

  Create recoder object

  `codes` give a sequence of code, alias sequences `fields` are names by which the entries in these sequences can be accessed.

  By default `fields` gives the first column the name “code”. The first column is the vector of first entries in each of the sequences found in `codes`. Thence you can get the equivalent first column value with ob.code[value], where value can be a first column value, or a value in any of the other columns in that sequence.

  You can give other columns names too, and access them in the same way - see the examples in the class docstring.

  Parameters:
    codes sequence of sequences

      Each sequence defines values (codes) that are equivalent

    fields{(‘code’,) string sequence}, optional

      names by which elements in sequences can be accessed

    map_maker: callable, optional

      constructor for dict-like objects used to store key value pairs. Default is `dict`. `map_maker()` generates an empty mapping. The mapping need only implement `__getitem__, __setitem__, keys, values`.

  __init__(codes: ~typing.Sequence[~typing.Sequence[~typing.Hashable]], fields: ~typing.Sequence[str] = ('code',), map_maker: type[~typing.Mapping[~typing.Hashable, ~typing.Hashable]] = <class 'dict'>)

    Create recoder object

    `codes` give a sequence of code, alias sequences `fields` are names by which the entries in these sequences can be accessed.

    By default `fields` gives the first column the name “code”. The first column is the vector of first entries in each of the sequences found in `codes`. Thence you can get the equivalent first column value with ob.code[value], where value can be a first column value, or a value in any of the other columns in that sequence.

    You can give other columns names too, and access them in the same way - see the examples in the class docstring.

    Parameters:
      codes sequence of sequences

        Each sequence defines values (codes) that are equivalent

      fields{(‘code’,) string sequence}, optional

        names by which elements in sequences can be accessed

      map_maker: callable, optional

        constructor for dict-like objects used to store key value pairs. Default is `dict`. `map_maker()` generates an empty mapping. The mapping need only implement `__getitem__, __setitem__, keys, values`.

  add_codes(code_syn_seqs: Sequence[Sequence[Hashable]]) → None

    Add codes to object

    Parameters:
      code_syn_seqs sequence

        sequence of sequences, where each sequence `S = code_syn_seqs[n]` for n in 0..len(code_syn_seqs), is a sequence giving values in the same order as `self.fields`. Each S should be at least of the same length as `self.fields`. After this call, if `self.fields == ['field1', 'field2'], then ``self.field1[S[n]] == S[0]` for all n in 0..len(S) and `self.field2[S[n]] == S[1]` for all n in 0..len(S).

    Examples

        >>> code_syn_seqs = ((2, 'two'), (1, 'one'))
        >>> rc = Recoder(code_syn_seqs)
        >>> rc.value_set() == set((1,2))
        True
        >>> rc.add_codes(((3, 'three'), (1, 'first')))
        >>> rc.value_set() == set((1,2,3))
        True
        >>> print(rc.value_set())  # set is actually ordered
        OrderedSet([2, 1, 3])

  fields: tuple[str, ...]
  keys()

    Return all available code and alias values

    Returns same value as `obj.field1.keys()` and, with the default initializing `fields` argument of fields=(‘code’,), this will return the same as `obj.code.keys()`

        >>> codes = ((1, 'one'), (2, 'two'), (1, 'repeat value'))
        >>> k = Recoder(codes).keys()
        >>> set(k) == set([1, 2, 'one', 'repeat value', 'two'])
        True

  value_set(name: str | None = None) → OrderedSet

    Return OrderedSet of possible returned values for column

    By default, the column is the first column.

    Returns same values as `set(obj.field1.values())` and, with the default initializing``fields`` argument of fields=(‘code’,), this will return the same as `set(obj.code.values())`

    Parameters:
      name{None, string}

        Where default of none gives result for first column

      >>> codes = ((1, ‘one’), (2, ‘two’), (1, ‘repeat value’))
      >>> vs = Recoder(codes).value_set()
      >>> vs == set([1, 2]) # Sets are not ordered, hence this test
      True
      >>> rc = Recoder(codes, fields=(‘code’, ‘label’))
      >>> rc.value_set(‘label’) == set((‘one’, ‘two’, ‘repeat value’))
      True

## apply_read_scaling

nibabel.volumeutils.apply_read_scaling(arr: np.ndarray, slope: Scalar | None = None, inter: Scalar | None = None) → np.ndarray

  Apply scaling in slope and inter to array arr

  This is for loading the array from a file (as opposed to the reverse scaling when saving an array to file)

  Return data will be `arr * slope + inter`. The trick is that we have to find a good precision to use for applying the scaling. The heuristic is that the data is always upcast to the higher of the types from arr, `slope, inter if slope and / or inter are not default values. If the dtype of arr is an integer, then we assume the data more or less fills the integer range, and upcast to a type such that the min, max of `arr.dtype` * scale + inter, will be finite.

  Parameters:
    arr array-like
    slope None or float, optional

      slope value to apply to arr (`arr * slope + inter`). None corresponds to a value of 1.0

    inter None or float, optional

      intercept value to apply to arr (`arr * slope + inter`). None corresponds to a value of 0.0

  Returns:
    ret array

      array with scaling applied. Maybe upcast in order to give room for the scaling. If scaling is default (1, 0), then ret may be arr `ret is arr`.

## array_from_file

nibabel.volumeutils.array_from_file(shape: tuple[int, ...], in_dtype: np.dtype[DT], infile: io.IOBase, offset: int = 0, order: ty.Literal['C', 'F'] = 'F', mmap: bool | ty.Literal['c', 'r', 'r+'] = True) → npt.NDArray[DT]

  Get array from file with specified shape, dtype and file offset

  Parameters:
    shape sequence

      sequence specifying output array shape

    in_dtype numpy dtype

      fully specified numpy dtype, including correct endianness

    infile file-like

      open file-like object implementing at least read() and seek()

    offset int, optional

      offset in bytes into infile to start reading array data. Default is 0

    order{‘F’, ‘C’} string

      order in which to write data. Default is ‘F’ (fortran order).

    mmap{True, False, ‘c’, ‘r’, ‘r+’}

      mmap controls the use of numpy memory mapping for reading data. If False, do not try numpy `memmap` for data array. If one of {‘c’, ‘r’, ‘r+’}, try numpy memmap with `mode=mmap`. A mmap value of True gives the same behavior as `mmap='c'`. If infile cannot be memory-mapped, ignore mmap value and read array from file.

  Returns:
    arr array-like

      array like object that can be sliced, containing data

  Examples

      >>> from io import BytesIO
      >>> bio = BytesIO()
      >>> arr = np.arange(6).reshape(1,2,3)
      >>> _ = bio.write(arr.tobytes('F'))  # outputs int
      >>> arr2 = array_from_file((1,2,3), arr.dtype, bio)
      >>> np.all(arr == arr2)
      True
      >>> bio = BytesIO()
      >>> _ = bio.write(b' ' * 10)
      >>> _ = bio.write(arr.tobytes('F'))
      >>> arr2 = array_from_file((1,2,3), arr.dtype, bio, 10)
      >>> np.all(arr == arr2)
      True

## array_to_file

nibabel.volumeutils.array_to_file(data: npt.ArrayLike, fileobj: io.IOBase, out_dtype: np.dtype | None = None, offset: int = 0, intercept: Scalar = 0.0, divslope: Scalar | None = 1.0, mn: Scalar | None = None, mx: Scalar | None = None, order: ty.Literal['C', 'F'] = 'F', nan2zero: bool = True) → None

  Helper function for writing arrays to file objects

  Writes arrays as scaled by intercept and divslope, and clipped at (prescaling) mn minimum, and mx maximum.

  - Clip data array at min mn, max max where there are not None -> `clipped` (this is pre scale clipping)

  - Scale `clipped` with `clipped_scaled = (clipped - intercept) / divslope`

  - Clip `clipped_scaled` to fit into range of out_dtype (post scale clipping) -> `clipped_scaled_clipped`

  - If converting to integer out_dtype and nan2zero is True, set NaN values in `clipped_scaled_clipped` to 0

  - Write `clipped_scaled_clipped_n2z` to fileobj fileobj starting at offset offset in memory layout order

  Parameters:
    data array-like

      array or array-like to write.

    fileobj file-like

      file-like object implementing `write` method.

    out_dtype None or dtype, optional

      dtype to write array as. Data array will be coerced to this dtype before writing. If None (default) then use input data type.

    offset None or int, optional

      offset into fileobj at which to start writing data. Default is 0. None means start at current file position

    intercept scalar, optional

      scalar to subtract from data, before dividing by `divslope`. Default is 0.0

    divslope None or scalar, optional

      scalefactor to divide data by before writing. Default is 1.0. If None, there is no valid data, we write zeros.

    mn scalar, optional

      minimum threshold in (unscaled) data, such that all data below this value are set to this value. Default is None (no threshold). The typical use is to set -np.inf in the data to have this value (which might be the minimum non-finite value in the data).

    mx scalar, optional

      maximum threshold in (unscaled) data, such that all data above this value are set to this value. Default is None (no threshold). The typical use is to set np.inf in the data to have this value (which might be the maximum non-finite value in the data).

    order{‘F’, ‘C’}, optional

      memory order to write array. Default is ‘F’

    nan2zero{True, False}, optional

      Whether to set NaN values to 0 when writing integer output. Defaults to True. If False, NaNs will be represented as numpy does when casting; this depends on the underlying C library and is undefined. In practice nan2zero == False might be a good choice when you completely sure there will be no NaNs in the data. This value ignored for float output types. NaNs are treated as zero before applying intercept and divslope - so an array `[np.nan]` with an intercept of 10 becomes `[-10]` after conversion to integer out_dtype with nan2zero set. That is because you will likely apply divslope and intercept in reverse order when reading the data back, returning the zero you probably expected from the input NaN.

  Examples

      >>> from io import BytesIO
      >>> sio = BytesIO()
      >>> data = np.arange(10, dtype=np.float64)
      >>> array_to_file(data, sio, np.float64)
      >>> sio.getvalue() == data.tobytes('F')
      True
      >>> _ = sio.truncate(0); _ = sio.seek(0)  # outputs 0
      >>> array_to_file(data, sio, np.int16)
      >>> sio.getvalue() == data.astype(np.int16).tobytes()
      True
      >>> _ = sio.truncate(0); _ = sio.seek(0)
      >>> array_to_file(data.byteswap(), sio, np.float64)
      >>> sio.getvalue() == data.byteswap().tobytes('F')
      True
      >>> _ = sio.truncate(0); _ = sio.seek(0)
      >>> array_to_file(data, sio, np.float64, order='C')
      >>> sio.getvalue() == data.tobytes('C')
      True

## best_write_scale_ftype

nibabel.volumeutils.best_write_scale_ftype(arr: np.ndarray, slope: npt.ArrayLike = 1.0, inter: npt.ArrayLike = 0.0, default: type[np.number] = <class 'numpy.float32'>) → type[np.floating]

  Smallest float type to contain range of `arr` after scaling

  Scaling that will be applied to `arr` is `(arr - inter) / slope`.

  Note that `slope` and `inter` get promoted to 1D arrays for this purpose to avoid the numpy scalar casting rules, which prevent scalars upcasting the array.

  Parameters:
    arr array-like

      array that will be scaled

    slope array-like, optional

      scalar such that output array will be `(arr - inter) / slope`.

    inter array-like, optional

      scalar such that output array will be `(arr - inter) / slope`

    default numpy type, optional

      minimum float type to return

  Returns:
    ftype numpy type

      Best floating point type for scaling. If no floating point type prevents overflow, return the top floating point type. If the input array `arr` already contains inf values, return the greater of the input type and the default type.

  Examples

      >>> arr = np.array([0, 1, 2], dtype=np.int16)
      >>> best_write_scale_ftype(arr, 1, 0) is np.float32
      True

  Specify higher default return value

      >>> best_write_scale_ftype(arr, 1, 0, default=np.float64) is np.float64
      True

  Even large values that don’t overflow don’t change output

      >>> arr = np.array([0, np.finfo(np.float32).max], dtype=np.float32)
      >>> best_write_scale_ftype(arr, 1, 0) is np.float32
      True

  Scaling > 1 reduces output values, so no upcast needed

      >>> best_write_scale_ftype(arr, np.float32(2), 0) is np.float32
      True

  Scaling < 1 increases values, so upcast may be needed (and is here)

      >>> best_write_scale_ftype(arr, np.float32(0.5), 0) is np.float64
      True

## better_float_of

nibabel.volumeutils.better_float_of(first: npt.DTypeLike, second: npt.DTypeLike, default: type[np.floating] = <class 'numpy.float32'>) → type[np.floating]

  Return more capable float type of first and second

  Return default if neither of first or second is a float

  Parameters:
    first numpy type specifier

      Any valid input to np.dtype()`

    second numpy type specifier

      Any valid input to np.dtype()`

    default numpy type specifier, optional

      Any valid input to np.dtype()`

  Returns:
    better_type numpy type

      More capable of first or second if both are floats; if only one is a float return that, otherwise return default.

  Examples

      >>> better_float_of(np.float32, np.float64) is np.float64
      True
      >>> better_float_of(np.float32, 'i4') is np.float32
      True
      >>> better_float_of('i2', 'u4') is np.float32
      True
      >>> better_float_of('i2', 'u4', np.float64) is np.float64
      True

## finite_range

nibabel.volumeutils.finite_range(arr: npt.ArrayLike, check_nan: Literal[False] = False) → tuple[Scalar, Scalar]
nibabel.volumeutils.finite_range(arr: npt.ArrayLike, check_nan: Literal[True]) → tuple[Scalar, Scalar, bool]

  Get range (min, max) or range and flag (min, max, has_nan) from arr

  Parameters:
    arr array-like
    check_nan{False, True}, optional

      Whether to return third output, a bool signaling whether there are NaN values in arr

  Returns:
    mn scalar

      minimum of values in (flattened) array

    mx scalar

      maximum of values in (flattened) array

    has_nan bool

      Returned if check_nan is True. has_nan is True if there are one or more NaN values in arr

  Examples

      >>> a = np.array([[-1, 0, 1],[np.inf, np.nan, -np.inf]])
      >>> finite_range(a)
      (-1.0, 1.0)
      >>> a = np.array([[-1, 0, 1],[np.inf, np.nan, -np.inf]])
      >>> finite_range(a, check_nan=True)
      (-1.0, 1.0, True)
      >>> a = np.array([[np.nan],[np.nan]])
      >>> finite_range(a) == (np.inf, -np.inf)
      True
      >>> a = np.array([[-3, 0, 1],[2,-1,4]], dtype=int)
      >>> finite_range(a)
      (-3, 4)
      >>> a = np.array([[1, 0, 1],[2,3,4]], dtype=np.uint)
      >>> finite_range(a)
      (0, 4)
      >>> a = a + 1j
      >>> finite_range(a)
      (1j, (4+1j))
      >>> a = np.zeros((2,), dtype=[('f1', 'i2')])
      >>> finite_range(a)
      Traceback (most recent call last):
         ...
      TypeError: Can only handle numeric types

## fname_ext_ul_case

nibabel.volumeutils.fname_ext_ul_case(fname: str) → str

  fname with ext changed to upper / lower case if file exists

  Check for existence of fname. If it does exist, return unmodified. If it doesn’t, check for existence of fname with case changed from lower to upper, or upper to lower. Return this modified fname if it exists. Otherwise return fname unmodified

  Parameters:
    fname str

      filename.

  Returns:
    mod_fname str

      filename, maybe with extension of opposite case

## int_scinter_ftype

nibabel.volumeutils.int_scinter_ftype(ifmt: np.dtype[np.integer] | type[np.integer], slope: npt.ArrayLike = 1.0, inter: npt.ArrayLike = 0.0, default: type[np.floating] = <class 'numpy.float32'>) → type[np.floating]

  float type containing int type ifmt * slope + inter

  Return float type that can represent the max and the min of the ifmt type after multiplication with slope and addition of inter with something like `np.array([imin, imax], dtype=ifmt) * slope + inter`.

  Note that `slope` and `inter` get promoted to 1D arrays for this purpose to avoid the numpy scalar casting rules, which prevent scalars upcasting the array.

  Parameters:
    ifmt object

      numpy integer type (e.g. np.int32)

    slope float, optional

      slope, default 1.0

    inter float, optional

      intercept, default 0.0

    default_out object, optional

      numpy floating point type, default is `np.float32`

  Returns:
    ftype object

      numpy floating point type

  Notes

  It is difficult to make floats overflow with just addition because the deltas are so large at the extremes of floating point. For example:

      >>> arr = np.array([np.finfo(np.float32).max], dtype=np.float32)
      >>> res = arr + np.iinfo(np.int16).max
      >>> arr == res
      array([ True])

  Examples

      >>> int_scinter_ftype(np.int8, 1.0, 0.0) == np.float32
      True
      >>> int_scinter_ftype(np.int8, 1e38, 0.0) == np.float64
      True

## make_dt_codes

nibabel.volumeutils.make_dt_codes(codes_seqs: Sequence[Sequence]) → Recoder

  Create full dt codes Recoder instance from datatype codes

  Include created numpy dtype (from numpy type) and opposite endian numpy dtype

  Parameters:
    codes_seqs sequence of sequences

      contained sequences make be length 3 or 4, but must all be the same length. Elements are data type code, data type name, and numpy type (such as `np.float32`). The fourth element is the nifti string representation of the code (e.g. “NIFTI_TYPE_FLOAT32”)

  Returns:
    rec `Recoder` instance

      Recoder that, by default, returns `code` when indexed with any of the corresponding code, name, type, dtype, or swapped dtype. You can also index with `niistring` values if codes_seqs had sequences of length 4 instead of 3.

## pretty_mapping

nibabel.volumeutils.pretty_mapping(mapping: ty.Mapping[K, V], getterfunc: ty.Callable[[ty.Mapping[K, V], K], V] | None = None) → str

  Make pretty string from mapping

  Adjusts text column to print values on basis of longest key. Probably only sensible if keys are mainly strings.

  You can pass in a callable that does clever things to get the values out of the mapping, given the names. By default, we just use `__getitem__`

  Parameters:
    mapping mapping

      implementing iterator returning keys and .items()

    getterfunc None or callable

      callable taking two arguments, `obj` and `key` where `obj` is the passed mapping. If None, just use `lambda obj, key: obj[key]`

  Returns:
    str string

  Examples

      >>> d = {'a key': 'a value'}
      >>> print(pretty_mapping(d))
      a key  : a value
      >>> class C: # to control ordering, show get_ method
      ...     def __iter__(self):
      ...         return iter(('short_field','longer_field'))
      ...     def __getitem__(self, key):
      ...         if key == 'short_field':
      ...             return 0
      ...         if key == 'longer_field':
      ...             return 'str'
      ...     def get_longer_field(self):
      ...         return 'method string'
      >>> def getter(obj, key):
      ...     # Look for any 'get_<name>' methods
      ...     try:
      ...         return obj.__getattribute__('get_' + key)()
      ...     except AttributeError:
      ...         return obj[key]
      >>> print(pretty_mapping(C(), getter))
      short_field   : 0
      longer_field  : method string

## rec2dict

nibabel.volumeutils.rec2dict(rec: record) → dict[str, generic | ndarray]

  Convert recarray to dictionary

  Also converts scalar values to scalars

  Parameters:
    rec ndarray

      structured ndarray

  Returns:
    dct dict

      dict with key, value pairs as for rec

  Examples

      >>> r = np.zeros((), dtype = [('x', 'i4'), ('s', 'S10')])
      >>> d = rec2dict(r)
      >>> d == {'x': 0, 's': b''}
      True

## seek_tell

nibabel.volumeutils.seek_tell(fileobj: io.IOBase, offset: int, write0: bool = False) → None

  Seek in fileobj or check we’re in the right place already

  Parameters:
    fileobj file-like

      object implementing `seek` and (if seek raises an OSError) `tell`

    offset int

      position in file to which to seek

    write0{False, True}, optional

      If True, and standard seek fails, try to write zeros to the file to reach offset. This can be useful when writing bz2 files, that cannot do write seeks.

## shape_zoom_affine

nibabel.volumeutils.shape_zoom_affine(shape: Sequence[int] | ndarray, zooms: Sequence[float] | ndarray, x_flip: bool = True) → ndarray

  Get affine implied by given shape and zooms

  We get the translations from the center of the image (implied by shape).

  Parameters:
    shape(N,) array-like

      shape of image data. `N` is the number of dimensions

    zooms(N,) array-like

      zooms (voxel sizes) of the image

    x_flip{True, False}

      whether to flip the X row of the affine. Corresponds to radiological storage on disk.

  Returns:
    aff(4,4) array

      affine giving correspondence of voxel coordinates to mm coordinates, taking the center of the image as origin

  Examples

      >>> shape = (3, 5, 7)
      >>> zooms = (3, 2, 1)
      >>> shape_zoom_affine((3, 5, 7), (3, 2, 1))
      array([[-3.,  0.,  0.,  3.],
             [ 0.,  2.,  0., -4.],
             [ 0.,  0.,  1., -3.],
             [ 0.,  0.,  0.,  1.]])
      >>> shape_zoom_affine((3, 5, 7), (3, 2, 1), False)
      array([[ 3.,  0.,  0., -3.],
             [ 0.,  2.,  0., -4.],
             [ 0.,  0.,  1., -3.],
             [ 0.,  0.,  0.,  1.]])

## working_type

nibabel.volumeutils.working_type(in_type: npt.DTypeLike, slope: npt.ArrayLike = 1.0, inter: npt.ArrayLike = 0.0) → type[np.number]

  Return array type from applying slope, inter to array of in_type

  Numpy type that results from an array of type in_type being combined with slope and inter. It returns something like the dtype type of `((np.zeros((2,), dtype=in_type) - inter) / slope)`, but ignoring the actual values of slope and inter.

  Note that you would not necessarily get the same type by applying slope and inter the other way round. Also, you’ll see that the order in which slope and inter are applied is the opposite of the order in which they are passed.

  Parameters:
    in_type numpy type specifier

      Numpy type of input array. Any valid input for `np.dtype()`

    slope scalar, optional

      slope to apply to array. If 1.0 (default), ignore this value and its type.

    inter scalar, optional

      intercept to apply to array. If 0.0 (default), ignore this value and its type.

  Returns:
    wtype: numpy type

      Numpy type resulting from applying inter and slope to array of type in_type.

## write_zeros

nibabel.volumeutils.write_zeros(fileobj: io.IOBase, count: int, block_size: int = 8194) → None

  Write count zero bytes to fileobj

  Parameters:
    fileobj file-like object

      with `write` method

    count int

      number of bytes to write

    block_size int, optional

      largest continuous block to write.