/usr/lib/python3/dist-packages/nibabel/arrayproxy.py is in python3-nibabel 2.0.2-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | # emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
# See COPYING file distributed along with the NiBabel package for the
# copyright and license terms.
#
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
""" Array proxy base class
The proxy API is - at minimum:
* The object has a read-only attribute ``shape``
* read only ``is_proxy`` attribute / property set to True
* the object returns the data array from ``np.asarray(prox)``
* returns array slice from ``prox[<slice_spec>]`` where ``<slice_spec>`` is any
ndarray slice specification that does not use numpy 'advanced indexing'.
* modifying no object outside ``obj`` will affect the result of
``np.asarray(obj)``. Specifically:
* Changes in position (``obj.tell()``) of passed file-like objects will
not affect the output of from ``np.asarray(proxy)``.
* if you pass a header into the __init__, then modifying the original
header will not affect the result of the array return.
See :mod:`nibabel.tests.test_proxy_api` for proxy API conformance checks.
"""
import warnings
from .volumeutils import BinOpener, array_from_file, apply_read_scaling
from .fileslice import fileslice
from .keywordonly import kw_only_meth
class ArrayProxy(object):
""" Class to act as proxy for the array that can be read from a file
The array proxy allows us to freeze the passed fileobj and header such that
it returns the expected data array.
This implementation assumes a contiguous array in the file object, with one
of the numpy dtypes, starting at a given file position ``offset`` with
single ``slope`` and ``intercept`` scaling to produce output values.
The class ``__init__`` requires a ``header`` object with methods:
* get_data_shape
* get_data_dtype
* get_data_offset
* get_slope_inter
The header should also have a 'copy' method. This requirement will go away
when the deprecated 'header' propoerty goes away.
This implementation allows us to deal with Analyze and its variants,
including Nifti1, and with the MGH format.
Other image types might need more specific classes to implement the API.
See :mod:`nibabel.minc1`, :mod:`nibabel.ecat` and :mod:`nibabel.parrec` for
examples.
"""
# Assume Fortran array memory layout
order = 'F'
@kw_only_meth(2)
def __init__(self, file_like, header, mmap=True):
""" Initialize array proxy instance
Parameters
----------
file_like : object
File-like object or filename. If file-like object, should implement
at least ``read`` and ``seek``.
header : object
Header object implementing ``get_data_shape``, ``get_data_dtype``,
``get_data_offset``, ``get_slope_inter``
mmap : {True, False, 'c', 'r'}, optional, keyword only
`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'}, try numpy memmap with ``mode=mmap``. A `mmap` value of
True gives the same behavior as ``mmap='c'``. If `file_like`
cannot be memory-mapped, ignore `mmap` value and read array from
file.
scaling : {'fp', 'dv'}, optional, keyword only
Type of scaling to use - see header ``get_data_scaling`` method.
"""
if mmap not in (True, False, 'c', 'r'):
raise ValueError("mmap should be one of {True, False, 'c', 'r'}")
self.file_like = file_like
# Copies of values needed to read array
self._shape = header.get_data_shape()
self._dtype = header.get_data_dtype()
self._offset = header.get_data_offset()
self._slope, self._inter = header.get_slope_inter()
self._slope = 1.0 if self._slope is None else self._slope
self._inter = 0.0 if self._inter is None else self._inter
self._mmap = mmap
# Reference to original header; we will remove this soon
self._header = header.copy()
@property
def header(self):
warnings.warn('We will remove the header property from proxies soon',
FutureWarning,
stacklevel=2)
return self._header
@property
def shape(self):
return self._shape
@property
def is_proxy(self):
return True
@property
def slope(self):
return self._slope
@property
def inter(self):
return self._inter
@property
def offset(self):
return self._offset
def get_unscaled(self):
''' Read of data from file
This is an optional part of the proxy API
'''
with BinOpener(self.file_like) as fileobj:
raw_data = array_from_file(self._shape,
self._dtype,
fileobj,
offset=self._offset,
order=self.order,
mmap=self._mmap)
return raw_data
def __array__(self):
# Read array and scale
raw_data = self.get_unscaled()
return apply_read_scaling(raw_data, self._slope, self._inter)
def __getitem__(self, slicer):
with BinOpener(self.file_like) as fileobj:
raw_data = fileslice(fileobj,
slicer,
self._shape,
self._dtype,
self._offset,
order=self.order)
# Upcast as necessary for big slopes, intercepts
return apply_read_scaling(raw_data, self._slope, self._inter)
def is_proxy(obj):
""" Return True if `obj` is an array proxy
"""
try:
return obj.is_proxy
except AttributeError:
return False
|