1
""" File-based images that have data arrays
2

3
The class:`DataObjImage` class defines an image that extends the
4
:class:`FileBasedImage` by adding an array-like object, named ``dataobj``.
5
This can either be an actual numpy array, or an object that:
6

7
* returns an array from ``numpy.asanyarray(obj)``;
8
* has an attribute or property ``shape``.
9
"""
10

11 33
import numpy as np
12

13 33
from .filebasedimages import FileBasedImage
14 33
from .deprecated import deprecate_with_version
15

16

17 33
class DataobjImage(FileBasedImage):
18
    """ Template class for images that have dataobj data stores"""
19

20 33
    def __init__(self, dataobj, header=None, extra=None, file_map=None):
21
        """ Initialize dataobj image
22

23
        The datobj image is a combination of (dataobj, header), with optional
24
        metadata in `extra`, and filename / file-like objects contained in the
25
        `file_map` mapping.
26

27
        Parameters
28
        ----------
29
        dataobj : object
30
           Object containg image data.  It should be some object that retuns an
31
           array from ``np.asanyarray``.  It should have ``shape`` and ``ndim``
32
           attributes or properties
33
        header : None or mapping or header instance, optional
34
           metadata for this image format
35
        extra : None or mapping, optional
36
           metadata to associate with image that cannot be stored in the
37
           metadata of this image type
38
        file_map : mapping, optional
39
           mapping giving file information for this image format
40
        """
41 33
        super(DataobjImage, self).__init__(header=header, extra=extra,
42
                                           file_map=file_map)
43 33
        self._dataobj = dataobj
44 33
        self._fdata_cache = None
45 33
        self._data_cache = None
46

47 33
    @property
48 5
    def dataobj(self):
49 33
        return self._dataobj
50

51 33
    @property
52 33
    @deprecate_with_version('_data attribute not part of public API. '
53
                            'please use "dataobj" property instead.',
54
                            '2.0', '4.0')
55 5
    def _data(self):
56 33
        return self._dataobj
57

58 33
    @deprecate_with_version('get_data() is deprecated in favor of get_fdata(),'
59
                            ' which has a more predictable return type. To '
60
                            'obtain get_data() behavior going forward, use '
61
                            'numpy.asanyarray(img.dataobj).',
62
                            '3.0', '5.0')
63 33
    def get_data(self, caching='fill'):
64
        """ Return image data from image with any necessary scaling applied
65

66
        .. WARNING::
67

68
            We recommend you use the ``get_fdata`` method instead of the
69
            ``get_data`` method, because it is easier to predict the return
70
            data type.  ``get_data`` will be deprecated around November 2019
71
            and removed around November 2021.
72

73
            If you don't care about the predictability of the return data type,
74
            and you want the minimum possible data size in memory, you can
75
            replicate the array that would be returned by ``img.get_data()`` by
76
            using ``np.asanyarray(img.dataobj)``.
77

78
        The image ``dataobj`` property can be an array proxy or an array.  An
79
        array proxy is an object that knows how to load the image data from
80
        disk.  An image with an array proxy ``dataobj`` is a *proxy image*; an
81
        image with an array in ``dataobj`` is an *array image*.
82

83
        The default behavior for ``get_data()`` on a proxy image is to read the
84
        data from the proxy, and store in an internal cache.  Future calls to
85
        ``get_data`` will return the cached array.  This is the behavior
86
        selected with `caching` == "fill".
87

88
        Once the data has been cached and returned from an array proxy, if you
89
        modify the returned array, you will also modify the cached array
90
        (because they are the same array).  Regardless of the `caching` flag,
91
        this is always true of an array image.
92

93
        Parameters
94
        ----------
95
        caching : {'fill', 'unchanged'}, optional
96
            See the Notes section for a detailed explanation.  This argument
97
            specifies whether the image object should fill in an internal
98
            cached reference to the returned image data array. "fill" specifies
99
            that the image should fill an internal cached reference if
100
            currently empty.  Future calls to ``get_data`` will return this
101
            cached reference.  You might prefer "fill" to save the image object
102
            from having to reload the array data from disk on each call to
103
            ``get_data``.  "unchanged" means that the image should not fill in
104
            the internal cached reference if the cache is currently empty.  You
105
            might prefer "unchanged" to "fill" if you want to make sure that
106
            the call to ``get_data`` does not create an extra (cached)
107
            reference to the returned array.  In this case it is easier for
108
            Python to free the memory from the returned array.
109

110
        Returns
111
        -------
112
        data : array
113
            array of image data
114

115
        See also
116
        --------
117
        uncache: empty the array data cache
118

119
        Notes
120
        -----
121
        All images have a property ``dataobj`` that represents the image array
122
        data.  Images that have been loaded from files usually do not load the
123
        array data from file immediately, in order to reduce image load time
124
        and memory use.  For these images, ``dataobj`` is an *array proxy*; an
125
        object that knows how to load the image array data from file.
126

127
        By default (`caching` == "fill"), when you call ``get_data`` on a
128
        proxy image, we load the array data from disk, store (cache) an
129
        internal reference to this array data, and return the array.  The next
130
        time you call ``get_data``, you will get the cached reference to the
131
        array, so we don't have to load the array data from disk again.
132

133
        Array images have a ``dataobj`` property that already refers to an
134
        array in memory, so there is no benefit to caching, and the `caching`
135
        keywords have no effect.
136

137
        For proxy images, you may not want to fill the cache after reading the
138
        data from disk because the cache will hold onto the array memory until
139
        the image object is deleted, or you use the image ``uncache`` method.
140
        If you don't want to fill the cache, then always use
141
        ``get_data(caching='unchanged')``; in this case ``get_data`` will not
142
        fill the cache (store the reference to the array) if the cache is empty
143
        (no reference to the array).  If the cache is full, "unchanged" leaves
144
        the cache full and returns the cached array reference.
145

146
        The cache can affect the behavior of the image, because if the cache is
147
        full, or you have an array image, then modifying the returned array
148
        will modify the result of future calls to ``get_data()``.  For example
149
        you might do this:
150

151
        >>> import os
152
        >>> import nibabel as nib
153
        >>> from nibabel.testing import data_path
154
        >>> img_fname = os.path.join(data_path, 'example4d.nii.gz')
155

156
        >>> img = nib.load(img_fname) # This is a proxy image
157
        >>> nib.is_proxy(img.dataobj)
158
        True
159

160
        The array is not yet cached by a call to "get_data", so:
161

162
        >>> img.in_memory
163
        False
164

165
        After we call ``get_data`` using the default `caching` == 'fill', the
166
        cache contains a reference to the returned array ``data``:
167

168
        >>> data = img.get_data()
169
        >>> img.in_memory
170
        True
171

172
        We modify an element in the returned data array:
173

174
        >>> data[0, 0, 0, 0]
175
        0
176
        >>> data[0, 0, 0, 0] = 99
177
        >>> data[0, 0, 0, 0]
178
        99
179

180
        The next time we call 'get_data', the method returns the cached
181
        reference to the (modified) array:
182

183
        >>> data_again = img.get_data()
184
        >>> data_again is data
185
        True
186
        >>> data_again[0, 0, 0, 0]
187
        99
188

189
        If you had *initially* used `caching` == 'unchanged' then the returned
190
        ``data`` array would have been loaded from file, but not cached, and:
191

192
        >>> img = nib.load(img_fname)  # a proxy image again
193
        >>> data = img.get_data(caching='unchanged')
194
        >>> img.in_memory
195
        False
196
        >>> data[0, 0, 0] = 99
197
        >>> data_again = img.get_data(caching='unchanged')
198
        >>> data_again is data
199
        False
200
        >>> data_again[0, 0, 0, 0]
201
        0
202
        """
203 33
        if caching not in ('fill', 'unchanged'):
204 33
            raise ValueError('caching value should be "fill" or "unchanged"')
205 33
        if self._data_cache is not None:
206 33
            return self._data_cache
207 33
        data = np.asanyarray(self._dataobj)
208 33
        if caching == 'fill':
209 33
            self._data_cache = data
210 33
        return data
211

212 33
    def get_fdata(self, caching='fill', dtype=np.float64):
213
        """ Return floating point image data with necessary scaling applied
214

215
        The image ``dataobj`` property can be an array proxy or an array.  An
216
        array proxy is an object that knows how to load the image data from
217
        disk.  An image with an array proxy ``dataobj`` is a *proxy image*; an
218
        image with an array in ``dataobj`` is an *array image*.
219

220
        The default behavior for ``get_fdata()`` on a proxy image is to read
221
        the data from the proxy, and store in an internal cache.  Future calls
222
        to ``get_fdata`` will return the cached array.  This is the behavior
223
        selected with `caching` == "fill".
224

225
        Once the data has been cached and returned from an array proxy, if you
226
        modify the returned array, you will also modify the cached array
227
        (because they are the same array).  Regardless of the `caching` flag,
228
        this is always true of an array image.
229

230
        Parameters
231
        ----------
232
        caching : {'fill', 'unchanged'}, optional
233
            See the Notes section for a detailed explanation.  This argument
234
            specifies whether the image object should fill in an internal
235
            cached reference to the returned image data array. "fill" specifies
236
            that the image should fill an internal cached reference if
237
            currently empty.  Future calls to ``get_fdata`` will return this
238
            cached reference.  You might prefer "fill" to save the image object
239
            from having to reload the array data from disk on each call to
240
            ``get_fdata``.  "unchanged" means that the image should not fill in
241
            the internal cached reference if the cache is currently empty.  You
242
            might prefer "unchanged" to "fill" if you want to make sure that
243
            the call to ``get_fdata`` does not create an extra (cached)
244
            reference to the returned array.  In this case it is easier for
245
            Python to free the memory from the returned array.
246
        dtype : numpy dtype specifier
247
            A numpy dtype specifier specifying a floating point type.  Data is
248
            returned as this floating point type.  Default is ``np.float64``.
249

250
        Returns
251
        -------
252
        fdata : array
253
            Array of image data of data type `dtype`.
254

255
        See also
256
        --------
257
        uncache: empty the array data cache
258

259
        Notes
260
        -----
261
        All images have a property ``dataobj`` that represents the image array
262
        data.  Images that have been loaded from files usually do not load the
263
        array data from file immediately, in order to reduce image load time
264
        and memory use.  For these images, ``dataobj`` is an *array proxy*; an
265
        object that knows how to load the image array data from file.
266

267
        By default (`caching` == "fill"), when you call ``get_fdata`` on a
268
        proxy image, we load the array data from disk, store (cache) an
269
        internal reference to this array data, and return the array.  The next
270
        time you call ``get_fdata``, you will get the cached reference to the
271
        array, so we don't have to load the array data from disk again.
272

273
        Array images have a ``dataobj`` property that already refers to an
274
        array in memory, so there is no benefit to caching, and the `caching`
275
        keywords have no effect.
276

277
        For proxy images, you may not want to fill the cache after reading the
278
        data from disk because the cache will hold onto the array memory until
279
        the image object is deleted, or you use the image ``uncache`` method.
280
        If you don't want to fill the cache, then always use
281
        ``get_fdata(caching='unchanged')``; in this case ``get_fdata`` will not
282
        fill the cache (store the reference to the array) if the cache is empty
283
        (no reference to the array).  If the cache is full, "unchanged" leaves
284
        the cache full and returns the cached array reference.
285

286
        The cache can effect the behavior of the image, because if the cache is
287
        full, or you have an array image, then modifying the returned array
288
        will modify the result of future calls to ``get_fdata()``.  For example
289
        you might do this:
290

291
        >>> import os
292
        >>> import nibabel as nib
293
        >>> from nibabel.testing import data_path
294
        >>> img_fname = os.path.join(data_path, 'example4d.nii.gz')
295

296
        >>> img = nib.load(img_fname) # This is a proxy image
297
        >>> nib.is_proxy(img.dataobj)
298
        True
299

300
        The array is not yet cached by a call to "get_fdata", so:
301

302
        >>> img.in_memory
303
        False
304

305
        After we call ``get_fdata`` using the default `caching` == 'fill', the
306
        cache contains a reference to the returned array ``data``:
307

308
        >>> data = img.get_fdata()
309
        >>> img.in_memory
310
        True
311

312
        We modify an element in the returned data array:
313

314
        >>> data[0, 0, 0, 0]
315
        0.0
316
        >>> data[0, 0, 0, 0] = 99
317
        >>> data[0, 0, 0, 0]
318
        99.0
319

320
        The next time we call 'get_fdata', the method returns the cached
321
        reference to the (modified) array:
322

323
        >>> data_again = img.get_fdata()
324
        >>> data_again is data
325
        True
326
        >>> data_again[0, 0, 0, 0]
327
        99.0
328

329
        If you had *initially* used `caching` == 'unchanged' then the returned
330
        ``data`` array would have been loaded from file, but not cached, and:
331

332
        >>> img = nib.load(img_fname)  # a proxy image again
333
        >>> data = img.get_fdata(caching='unchanged')
334
        >>> img.in_memory
335
        False
336
        >>> data[0, 0, 0] = 99
337
        >>> data_again = img.get_fdata(caching='unchanged')
338
        >>> data_again is data
339
        False
340
        >>> data_again[0, 0, 0, 0]
341
        0.0
342
        """
343 33
        if caching not in ('fill', 'unchanged'):
344 33
            raise ValueError('caching value should be "fill" or "unchanged"')
345 33
        dtype = np.dtype(dtype)
346 33
        if not issubclass(dtype.type, np.inexact):
347 33
            raise ValueError(f'{dtype} should be floating point type')
348
        # Return cache if cache present and of correct dtype.
349 33
        if self._fdata_cache is not None:
350 33
            if self._fdata_cache.dtype.type == dtype.type:
351 33
                return self._fdata_cache
352
        # Always return requested data type
353
        # For array proxies, will attempt to confine data array to dtype
354
        # during scaling
355 33
        data = np.asanyarray(self._dataobj, dtype=dtype)
356 33
        if caching == 'fill':
357 33
            self._fdata_cache = data
358 33
        return data
359

360 33
    @property
361 5
    def in_memory(self):
362
        """ True when any array data is in memory cache
363

364
        There are separate caches for `get_data` reads and `get_fdata` reads.
365
        This property is True if either of those caches are set.
366
        """
367 33
        return (isinstance(self._dataobj, np.ndarray) or
368
                self._fdata_cache is not None or
369
                self._data_cache is not None)
370

371 33
    def uncache(self):
372
        """ Delete any cached read of data from proxied data
373

374
        Remember there are two types of images:
375

376
        * *array images* where the data ``img.dataobj`` is an array
377
        * *proxy images* where the data ``img.dataobj`` is a proxy object
378

379
        If you call ``img.get_fdata()`` on a proxy image, the result of reading
380
        from the proxy gets cached inside the image object, and this cache is
381
        what gets returned from the next call to ``img.get_fdata()``.  If you
382
        modify the returned data, as in::
383

384
            data = img.get_fdata()
385
            data[:] = 42
386

387
        then the next call to ``img.get_fdata()`` returns the modified array,
388
        whether the image is an array image or a proxy image::
389

390
            assert np.all(img.get_fdata() == 42)
391

392
        When you uncache an array image, this has no effect on the return of
393
        ``img.get_fdata()``, but when you uncache a proxy image, the result of
394
        ``img.get_fdata()`` returns to its original value.
395
        """
396 33
        self._fdata_cache = None
397 33
        self._data_cache = None
398

399 33
    @property
400 5
    def shape(self):
401 33
        return self._dataobj.shape
402

403 33
    @property
404 5
    def ndim(self):
405 33
        return self._dataobj.ndim
406

407 33
    @deprecate_with_version('get_shape method is deprecated.\n'
408
                            'Please use the ``img.shape`` property '
409
                            'instead.',
410
                            '1.2', '3.0')
411 5
    def get_shape(self):
412
        """ Return shape for image
413
        """
414 0
        return self.shape
415

416 33
    @classmethod
417 33
    def from_file_map(klass, file_map, *, mmap=True, keep_file_open=None):
418
        """ Class method to create image from mapping in ``file_map``
419

420
        .. deprecated:: 2.4.1
421
            ``keep_file_open='auto'`` is redundant with `False` and has
422
            been deprecated. It raises an error as of nibabel 3.0.
423

424
        Parameters
425
        ----------
426
        file_map : dict
427
            Mapping with (kay, value) pairs of (``file_type``, FileHolder
428
            instance giving file-likes for each file needed for this image
429
            type.
430
        mmap : {True, False, 'c', 'r'}, optional, keyword only
431
            `mmap` controls the use of numpy memory mapping for reading image
432
            array data.  If False, do not try numpy ``memmap`` for data array.
433
            If one of {'c', 'r'}, try numpy memmap with ``mode=mmap``.  A
434
            `mmap` value of True gives the same behavior as ``mmap='c'``.  If
435
            image data file cannot be memory-mapped, ignore `mmap` value and
436
            read array from file.
437
        keep_file_open : { None, True, False }, optional, keyword only
438
            `keep_file_open` controls whether a new file handle is created
439
            every time the image is accessed, or a single file handle is
440
            created and used for the lifetime of this ``ArrayProxy``. If
441
            ``True``, a single file handle is created and used. If ``False``,
442
            a new file handle is created every time the image is accessed.
443
            If ``file_map`` refers to an open file handle, this setting has no
444
            effect. The default value (``None``) will result in the value of
445
            ``nibabel.arrayproxy.KEEP_FILE_OPEN_DEFAULT`` being used.
446

447
        Returns
448
        -------
449
        img : DataobjImage instance
450
        """
451 0
        raise NotImplementedError
452

453 33
    @classmethod
454 33
    def from_filename(klass, filename, *, mmap=True, keep_file_open=None):
455
        """Class method to create image from filename `filename`
456

457
        .. deprecated:: 2.4.1
458
            ``keep_file_open='auto'`` is redundant with `False` and has
459
            been deprecated. It raises an error as of nibabel 3.0.
460

461
        Parameters
462
        ----------
463
        filename : str
464
            Filename of image to load
465
        mmap : {True, False, 'c', 'r'}, optional, keyword only
466
            `mmap` controls the use of numpy memory mapping for reading image
467
            array data.  If False, do not try numpy ``memmap`` for data array.
468
            If one of {'c', 'r'}, try numpy memmap with ``mode=mmap``.  A
469
            `mmap` value of True gives the same behavior as ``mmap='c'``.  If
470
            image data file cannot be memory-mapped, ignore `mmap` value and
471
            read array from file.
472
        keep_file_open : { None, True, False }, optional, keyword only
473
            `keep_file_open` controls whether a new file handle is created
474
            every time the image is accessed, or a single file handle is
475
            created and used for the lifetime of this ``ArrayProxy``. If
476
            ``True``, a single file handle is created and used. If ``False``,
477
            a new file handle is created every time the image is accessed.
478
            The default value (``None``) will result in the value of
479
            ``nibabel.arrayproxy.KEEP_FILE_OPEN_DEFAULT`` being used.
480

481
        Returns
482
        -------
483
        img : DataobjImage instance
484
        """
485 33
        if mmap not in (True, False, 'c', 'r'):
486 33
            raise ValueError("mmap should be one of {True, False, 'c', 'r'}")
487 33
        file_map = klass.filespec_to_file_map(filename)
488 33
        return klass.from_file_map(file_map, mmap=mmap,
489
                                   keep_file_open=keep_file_open)
490

491 33
    load = from_filename

Read our documentation on viewing source code .

Loading