Skip to content

ENH: Add SerializableImage class with to/from_bytes methods #644

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jun 22, 2019

Conversation

effigies
Copy link
Member

@effigies effigies commented Jul 2, 2018

Closes #639.

Implements to/from_bytes for single-image variants of NIfTI-1, NIfTI-2, GIFTI and CIFTI2 images, as well as MGH images.

Created an abstract class deriving from FileBasedImage that can be used in other images, if it becomes desirable, but didn't want to spend a ton of time nailing it down for image types I have an, at best, passing familiarity with.

@codecov-io
Copy link

codecov-io commented Jul 2, 2018

Codecov Report

Merging #644 into master will decrease coverage by 1.56%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #644      +/-   ##
=========================================
- Coverage   89.97%   88.4%   -1.57%     
=========================================
  Files          94     188      +94     
  Lines       12004   24038   +12034     
  Branches     2127    4254    +2127     
=========================================
+ Hits        10800   21251   +10451     
- Misses        862    2100    +1238     
- Partials      342     687     +345
Impacted Files Coverage Δ
nibabel/freesurfer/mghformat.py 95.45% <100%> (+0.01%) ⬆️
nibabel/__init__.py 93.75% <100%> (+0.13%) ⬆️
nibabel/filebasedimages.py 90.71% <100%> (+1.56%) ⬆️
nibabel/nifti1.py 91.18% <100%> (-0.3%) ⬇️
nibabel/gifti/gifti.py 95.23% <100%> (+0.01%) ⬆️
nibabel/environment.py 75% <0%> (-20%) ⬇️
nibabel/casting.py 85.59% <0%> (-1.7%) ⬇️
nibabel/dft.py 80.48% <0%> (-0.61%) ⬇️
nibabel/cmdline/parrec2nii.py 32.73% <0%> (ø) ⬆️
... and 95 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update eb3bd12...9eccf32. Read the comment docs.

@coveralls
Copy link

coveralls commented Jul 2, 2018

Coverage Status

Coverage increased (+0.02%) to 91.846% when pulling 2407015 on effigies:enh/serialize into be35aca on nipy:master.

@effigies
Copy link
Member Author

Current state: CIFTI issues. Not clear why it's not consistent with the file output, but may be due to to_file_map performing updates. Perhaps they're not idempotent.

Based on TimVanMourik/Armadillo#11, it seems that deserializing would be similarly useful.

Incidentally, I'm not committed to "(de)serialize" as function names. To be consistent with the rest of the API, we could use to_bytes and from_bytes. Let me know what y'all think.

@Shotgunosine
Copy link

Shotgunosine commented Jul 31, 2018 via email

@effigies effigies changed the title [WIP] ENH: Add FileBasedImage.serialize() [WIP] ENH: Add SerializableImage class with to/from_bytes methods Aug 11, 2018
@effigies effigies changed the title [WIP] ENH: Add SerializableImage class with to/from_bytes methods ENH: Add SerializableImage class with to/from_bytes methods Aug 14, 2018
@effigies
Copy link
Member Author

I think this is ready for a review, if anybody has some time. Pretty compact, in the end. I may not have used the testing machinery to its full potential, but hopefully it isn't too grotesque.

Copy link
Member

@matthew-brett matthew-brett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit worried by the heuristic of simply dumping the header and the image into the same file, if the format does not specify how that should be done.

Maybe we could allow file formats to specify how that should be done, but barf if they do not.

We could start by barfing with any file format that has more than one image, and then build up?


* from_bytes(bytestring) - make instance by deserializing a byte string

The following properties should hold:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you want this to look in the rendered doc? What happens with these indented lists?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually haven't built the docs here. I'll fiddle with this.


The following properties should hold:

* ``klass.from_bytes(bstr).to_bytes() == bstr``
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that true in general for round-tripping? For example, what happens with nifti images with image scaling? I think the scale factors may get recalculated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. I guess what we want is load-level idempotence, to the same level of precision as save/load cycles.:

img_a = klass.from_bytes(bstr)
img_b = klass.from_bytes(img_a.to_bytes())

np.allclose(img_a.get_fdata(), img_b.get_fdata())

Would the following be too strong, as well?

img_a.to_bytes() == img_b.to_bytes()

@effigies
Copy link
Member Author

effigies commented Sep 26, 2018

@matthew-brett I've addressed (or responded to) your comments, if you want to have another look.

@effigies effigies mentioned this pull request Oct 1, 2018
19 tasks
@effigies effigies added this to the 2.4.0 milestone Feb 12, 2019
@effigies effigies mentioned this pull request Mar 15, 2019
20 tasks
@effigies effigies modified the milestones: 2.4.0, 2.5.0 Mar 18, 2019
@effigies
Copy link
Member Author

I've updated this PR to do the following:

  1. The mixin methods to_bytes/from_bytes now check whether the class's files_types attribute indicates a multi-file image, and raises a NotImplementedError. This can be overridden by a multi-image class, but ensures we don't accidentally make promises we can't keep.
  2. Nifti1Pair was marked serializable, when it should have been Nifti1Image. This has been corrected.
  3. The header equality check in the API tests is now more specifically targeted to the one corner case, i.e., the abstract FileBasedHeader class.

@matthew-brett I believe this has addressed all of your comments. Please let me know if you have any further concerns.

I would like to get this in before the final Python 2 feature release (2.5.0) if possible.

If anybody else has some spare cycles, I'd appreciate a review on this one.

Copy link
Member

@matthew-brett matthew-brett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - yes - you've covered the stuff I was worried about.

fobj.write(img.to_bytes())

Images that consist of separate header and data files (e.g., Analyze
images) currently do not support this interface.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe say something about the fact that image types can override this class method to implement it, for multi-file images.

Images that consist of separate header and data files (e.g., Analyze
images) currently do not support this interface.
'''
@classmethod
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8 doesn't need a newline here?

@@ -700,7 +700,7 @@ def test_a2f_non_numeric():
# Some versions of numpy can cast structured types to float, others not
try:
arr.astype(float)
except ValueError:
except (TypeError, ValueError):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this change in this PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nope. Rebased onto the wrong branch.

@effigies
Copy link
Member Author

Thanks for the review, Matthew.

@effigies effigies merged commit 980f678 into nipy:master Jun 22, 2019
@effigies effigies deleted the enh/serialize branch June 22, 2019 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

FileBasedImage.serialize()
5 participants