FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

nibabel.load crashes for very large files on python 3, not python 2 · Issue #362 · nipy/nibabel · GitHub

Repository navigation

nibabel.load crashes for very large files on python 3, not python 2 #362

Closed

Description

Hi,

I just noticed the following error: loading a very large file (such as a resting state image from the HCP dataset, e.g. with dimensions (91, 109, 91, 1200)) works fine with nibabel and python 2.7. The same command on python 3.5 fails with the following error:

---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
<ipython-input-5-6d7286f1eb52> in <module>()
----> 1 in_vol = in_img.get_data()

/usr/local/lib/python3.5/dist-packages/nibabel/spatialimages.py in get_data(self, caching)
    580         if self._data_cache is not None:
    581             return self._data_cache
--> 582         data = np.asanyarray(self._dataobj)
    583         if caching == 'fill':
    584             self._data_cache = data

/usr/local/lib/python3.5/dist-packages/numpy/core/numeric.py in asanyarray(a, dtype, order)
    512 
    513     """
--> 514     return array(a, dtype, copy=False, order=order, subok=True)
    515 
    516 def ascontiguousarray(a, dtype=None):

/usr/local/lib/python3.5/dist-packages/nibabel/arrayproxy.py in __array__(self)
    142     def __array__(self):
    143         # Read array and scale
--> 144         raw_data = self.get_unscaled()
    145         return apply_read_scaling(raw_data, self._slope, self._inter)
    146 

/usr/local/lib/python3.5/dist-packages/nibabel/arrayproxy.py in get_unscaled(self)
    137                                        offset=self._offset,
    138                                        order=self.order,
--> 139                                        mmap=self._mmap)
    140         return raw_data
    141 

/usr/local/lib/python3.5/dist-packages/nibabel/volumeutils.py in array_from_file(shape, in_dtype, infile, offset, order, mmap)
    512     if hasattr(infile, 'readinto'):
    513         data_bytes = bytearray(n_bytes)
--> 514         n_read = infile.readinto(data_bytes)
    515         needs_copy = False
    516     else:

/usr/lib/python3.5/gzip.py in read(self, size)
    272             import errno
    273             raise OSError(errno.EBADF, "read() on write-only GzipFile object")
--> 274         return self._buffer.read(size)
    275 
    276     def read1(self, size=-1):

/usr/lib/python3.5/_compression.py in readinto(self, b)
     66     def readinto(self, b):
     67         with memoryview(b) as view, view.cast("B") as byte_view:
---> 68             data = self.read(len(byte_view))
     69             byte_view[:len(data)] = data
     70         return len(data)

/usr/lib/python3.5/gzip.py in read(self, size)
    467             buf = self._fp.read(io.DEFAULT_BUFFER_SIZE)
    468 
--> 469             uncompress = self._decompressor.decompress(buf, size)
    470             if self._decompressor.unconsumed_tail != b"":
    471                 self._fp.prepend(self._decompressor.unconsumed_tail)

OverflowError: Python int too large for C unsigned int

Happy to provide further info, not sure what is going on.

Activity

  1. effigies commented on Oct 20, 2015

    Member

    What specific file from HCP did you use to produce this crash?

    Also, can you try with Python 3.4? I don't believe nibabel is testing on 3.5, yet.

  2. surchs commented on Oct 20, 2015

    Author

    I used a resting state nifti file with all 4 sessions combined, hence the 1200 timepoints.

  3. matthew-brett commented on Oct 20, 2015

    Member

    Is it possible you have a 32-bit version of Python 3.5?

  4. surchs commented on Oct 20, 2015

    Author

    Pretty sure I got 64

    In [1]: import sys
    
    In [2]: sys.maxsize
    Out[2]: 9223372036854775807
    
  5. matthew-brett commented on Oct 20, 2015

    Member

    Can you try debug at the IPython prompt after the error, and find out what the value of size is in the traceback?

  6. surchs commented on Oct 20, 2015

    Author

    Sure

    ipdb> size
    4332617728
    
  7. matthew-brett commented on Oct 20, 2015

    Member

    As the error message says, that will just overflow a 32-bit unsigned integer.

    Would you mind putting a 1/0 or similar at line 512 of volumeutils.py in order to raise an error, then check the size of n_bytes when reading in Python 2.7? I guess it is the same?

  8. surchs commented on Oct 20, 2015

    Author

    It's pretty close:

    /usr/local/lib/python3.5/dist-packages/nibabel/volumeutils.py in array_from_file(shape, in_dtype, infile, offset, order, mmap)
        509         return np.array([])
        510     # Read data from file
    --> 511     1/0
        512     infile.seek(offset)
        513     if hasattr(infile, 'readinto'):
    
    ZeroDivisionError: division by zero
    
    In [5]: debug
    > /usr/local/lib/python3.5/dist-packages/nibabel/volumeutils.py(511)array_from_file()
        510     # Read data from file
    --> 511     1/0
        512     infile.seek(offset)
    
    ipdb> n_bytes
    4332619200
    ipdb> 
    
  9. matthew-brett commented on Oct 20, 2015

    Member

    So, that should overflow a 32-bit unsigned int as well. I guess the difference is in the Python implementations.

  10. surchs commented on Oct 20, 2015

    Author

    I'm not sure I'm following. Why would there be a 32 bit constraint when running on a 64 bit python shell?

  11. matthew-brett commented on Oct 20, 2015

    Member

    I don't know why that would happen, but it will depend on how Python implements gzip decompression. My guess is Python changed the implementation at some point, and the newer implementation happens to use an unsigned 32-bit int.

    Can you check other Python versions? Sorry to ask...

  12. surchs commented on Oct 20, 2015

    Author

    not easily, no. Sorry as well. I'll try on another machine. But this is seriously weird.

  13. matthew-brett commented on Oct 20, 2015

    Member

    Fair enough. I'll do my best to replicate on my laptop.

  14. effigies commented on Nov 8, 2015

    Member

    Any movement on this?

  15. bcipolli commented on Nov 8, 2015

    Contributor

    I will take a look at this.

  16. 18 remaining items

  17. bcipolli commented on Nov 14, 2015

    Contributor

    Sorry, I meant, do you get the same error as for an image, if you do:

    Ah I see, sorry. Yes, you get the same error.

  18. bcipolli commented on Nov 14, 2015

    Contributor

    Or, more succinctly:

            n_read = 0
            while n_read < n_bytes:
                to_read = (n_bytes - n_read) % (2**32 - 1) or (2**32 - 1)
                n_read += infile.readinto(data_bytes[n_read:n_read+to_read])
    
  19. matthew-brett commented on Nov 14, 2015

    Member

    I guess another option is to disable use of readinto for Python 3.5...

  20. bcipolli commented on Nov 14, 2015

    Contributor

    Any preference?

  21. matthew-brett commented on Nov 14, 2015

    Member

    I guess buffering will be more efficient. But - I think we need to cover the case where the read returns fewer bytes than we thought. How about something like:

    n_read = 0
    max_read = 2 ** 32 -1  # Max for unsigned 32-bit integer
    while (n_read > n_bytes):
        n_wanted = min([n_bytes - n_read, max_read])
        n_got = infile.readinto(data_bytes[n_read:n_read + n_wanted])
        n_read += n_got
        if n_got != n_wanted:
            break
    
  22. bcipolli commented on Nov 14, 2015

    Contributor

    Sounds great. Since it's gzip-specific, we should probably put that in _gzip_open, in openers.py.

    For testing, I think a unit test on array_from_file using a BytesIO object could be fastest--writing to disk sucks. If you can think of a way to do the Nifti1Image with BytesIO and avoid actually writing to disk, please let me know.

    I'll try the unit test in the meantime.

  23. bcipolli commented on Nov 14, 2015

    Contributor

    Er.... we'll also have to wrap GzipFile in that case... but it seems best to keep things gzip-specific...

  24. matthew-brett commented on Nov 14, 2015

    Member

    I see that it's possible to pass a file-like object to gzip.GzipFile, I guess it would be possible to use that to simulate a gzipped stream that is long, but we would still need a machine that could handle at least 4GB images in memory.

  25. bcipolli commented on Nov 14, 2015

    Contributor

    we would still need a machine that could handle at least 4GB images in memory.

    Well, I guess it'll have to be 4GB + the size of the GZIP output in memory, vs. 4GB (just the image in memory) that seems to be unavoidable. So 6GB vs. 4GB... the major concern is the time.

    As I'm running this code, I'm realizing that it's gzip itself that's slow. I'll decrease compression level in the test...

  26. matthew-brett commented on Nov 14, 2015

    Member

    I hate to say this now, but maybe we could do without a test here, on the basis that it's too time-consuming and memory intensive to test routinely.

    Or have a separate test repo, for these kinds of very long / slow tests, that we trigger travis on / run on the buildbots from time to time.

  27. bcipolli commented on Nov 14, 2015

    Contributor

    Sure thing. I want a test case to make sure things are working as expected before doing a PR, so ... it's useful, even if we disable the test by default.

  28. bcipolli commented on Nov 14, 2015

    Contributor

    How about an environment variable SLOW_TESTS that runs slow tests when set; otherwise, they're skipped?

  29. matthew-brett commented on Nov 14, 2015

    Member

    Sure. Even something like NIPY_EXTRA_TESTS which has a comma separated list of types of extra tests such as slow,bigmem.

  30. added a commit that references this issue on Mar 15, 2016
    cb88dce
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL