Skip to content

Invalid cutout/slice data for rice tile compressed images. #9955

Description

@Pharisaeus

EDIT: See #9955 (comment) for workaround.

Description

When slicing rice tile compressed image, the output data are invalid. In some cases this gets magically fixed if scale_back=True flag is passed when opening the fits file, but on some other cases it has no effect.

To clarify what I mean by valid and invalid data, see attached examples.

Expected behavior

This is what I would expect to get:
valid

Actual behavior

And this is what is produced:
invalid

(obviously the data are totally wrong, but the images show this much better than tables)

Steps to Reproduce

This can be easily reproduced by:

        outfile = "compressed_cut.fz"
        infile = "ADP.2020-02-12T10_26_25.400.fits.fz" # you can get the input file from http://dataportal.eso.org/dataPortal/file/ADP.2020-02-12T10:26:25.400
        hdus = fits.open(infile)
        slices = tuple([slice(start, stop) for (start, stop) in [(0, 4090), (0, 2040)]])
        hdus[1].data = hdus[1].data[slices]
        new_hdus = hdus[0:2]
        new_hdus.writeto(outfile, overwrite=True)
        hdus.close()

It's also pretty easy to see that the actual data saved to the file, are not what you'd expect. If we extend the above example to:

        outfile = "compressed_cut.fz"
        infile = "ADP.2020-02-12T10_26_25.400.fits.fz"  # you can get the input file from http://dataportal.eso.org/dataPortal/file/ADP.2020-02-12T10:26:25.400
        hdus = fits.open(infile)
        slices = tuple([slice(start, stop) for (start, stop) in [(0, 4090), (0, 2040)]])
        hdus[1].data = hdus[1].data[slices]
        new_hdus = hdus[0:2]
        print(hdus[1].data[1][0:100]) # data we're presumably saving to the file
        new_hdus.writeto(outfile, overwrite=True)
        hdus.close()

        hdus = fits.open(outfile)
        print(hdus[1].data[1][0:100]) # data we're reading back from file

We get two very different sets of data printed out, even though they should be the same.

Interestingly enough, if I cast the data to uint32, then the output is correct:

        infile = "ADP.2020-02-12T10_26_25.400.fits.fz" # you can get the input file from http://dataportal.eso.org/dataPortal/file/ADP.2020-02-12T10:26:25.400
        hdus = fits.open(infile)
        slices = tuple([slice(start, stop) for (start, stop) in [(0, 4090), (0, 2040)]])
        import numpy as np
        hdus[1].data = np.uint32(hdus[1].data[slices])
        new_hdus = hdus[0:2]
        new_hdus.writeto(outfile, overwrite=True)
        hdus.close()

But this in turn sets BSCALE to 1, and since the cast is to unsigned values, if there were negative values in the data, then they get turned into maxint, which is also a bit inconvenient.
I tried using the do_not_scale_image_data=True and scale_back=True flags but they have no effect.

Am I doing something wrong here?

System Details

Windows-10-10.0.17763-SP0
Python 3.7.3 (default, Apr 24 2019, 15:29:51) [MSC v.1915 64 bit (AMD64)]
Numpy 1.16.4
astropy 3.2.1

Activity

  1. pllim commented on Feb 17, 2020

    @pllim
    Member

    Re: Magically fixed -- Are you able to see a pattern in the data for when it gets "magically fix" or not?

    Did you also try this on a Linux machine, just in case the behavior is OS-dependent?

  2. Pharisaeus commented on Feb 17, 2020

    @Pharisaeus
    Author

    Re: Magically fixed -- Are you able to see a pattern in the data for when it gets "magically fix" or not?

    Example of the "magically fixed" file:

            outfile = "output_image_compressed.fits.fz"
            infile = "ADP.2018-11-15T10_35_06.695.fits.fz" # you can get the input file from http://dataportal.eso.org/dataPortal/file/ADP.2018-11-15T10:35:06.695
            hdus = fits.open(infile, scale_back=True)
            slices = tuple([slice(start, stop) for (start, stop) in [(0, 1000), (0, 1000)]])
            hdus[1].data = hdus[1].data[slices]
            new_hdus = hdus[0:2]
            print(hdus[1].data[1][0:100])
            new_hdus.writeto(outfile, overwrite=True)
            hdus.close()
    
            hdus = fits.open(outfile)
            print(hdus[1].data[1][0:100])
            hdus.close()

    If you pass the scale_back=True flag, then the outputs is correct. If you don't, then you can again see the characteristic pattern and output data are invalid. But this flag does not always help (see the file I mentioned in my first comment).

    Did you also try this on a Linux machine, just in case the behavior is OS-dependent?

    Yes, the same effects were produced on a Linux box:

    Linux-3.10.0-693.el7.x86_64-x86_64-with-redhat-7.4-Nitrogen
    Python 3.7.3 (default, Mar 27 2019, 22:11:17) [GCC 7.3.0]
    Numpy 1.17.2
    astropy 3.2.2

  3. added and removed on Feb 17, 2020
  4. pllim commented on Feb 17, 2020

    @pllim
    Member

    Thanks for all the info! Personally, I do not have time to look into this, but hopefully your info will help whoever who is interested to investigate this further.

    Also, please look at other issues related to FITS with Rice compression to see if you are experiencing one of the symptoms of those known issues.

  5. saimn commented on Feb 17, 2020

    @saimn
    Contributor

    Can you share a file to reproduce this issue ? In private if needed.

  6. saimn commented on Feb 17, 2020

    @saimn
    Contributor

    Also please try with the latest version of Astropy.

  7. Pharisaeus commented on Feb 17, 2020

    @Pharisaeus
    Author

    @saimn links to both files are provided with the code snippets. They are publicly available via ESO data portal:

    http://dataportal.eso.org/dataPortal/file/ADP.2020-02-12T10:26:25.400
    http://dataportal.eso.org/dataPortal/file/ADP.2018-11-15T10:35:06.695

    I will check with astropy 4.0

    edit: The same results with 4.0

  8. Pharisaeus commented on Feb 17, 2020

    @Pharisaeus
    Author

    Ok I found the reason why scale_back flag sometimes works and sometimes doesn't -> it all depends on the BSCALE keyword value. In my two example files the only difference is that in the first one BSCALE is 1. and thus parts of the astropy code related to scaling the image are not applied and the scale_back part is also skipped.

    Specifically I think this part is the key:

    self.data = np.array(np.around(self.data), dtype=_type)  # 0.7.7.1

    It does similar thing to what I was suggesting in my initial comment as strange way to "fix" the problem -> np.uint32(hdus[1].data[slices]), however this is only done in case BSCALE is not 1, so does not get applied for my first image, causing the outcome to be invalid. By changing the BSCALE to some 1+epsilon I was able to get valid results.

    The question is: why is there a special case for BSCALE=1, and why in such case astropy yields invalid results?

    edit: In fact just doing self.data = np.array(self.data) in this place is enough to trigger the valid results for BSCALE=1, which suggests that it's all about some action triggered by assigning data to HDU.

  9. saimn commented on Feb 18, 2020

    @saimn
    Contributor

    @Pharisaeus - Thanks, I missed the links above in the comments, sorry. So this is indeed a bug, I think the problem is that it goes through a feature of ImageHDU, where the data from an HDU that has not been modified is copied directly, which here cannot work with the tiles algorithm.

  10. Pharisaeus commented on Feb 18, 2020

    @Pharisaeus
    Author

    Thanks for looking into this!
    For now I will simply use the do_not_scale_image_data=True plus data = np.array(data) trick, because it seems to produce correct results in any case. Hopefully you can figure out how to fix this "properly" in the meantime.

  11. saimn commented on Feb 19, 2020

    @saimn
    Contributor

    Actually that's the same issue as #2150 (I thought it was different initially). So one easy workaround is to use something like:

    hdus[1].data = np.ascontiguousarray(hdus[1].data[slices])
    

    And the fix is basically the same thing but I need to write some tests, should be doable soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions