pytroll / satpy

Python package for earth-observing satellite data processing
http://satpy.readthedocs.org/en/latest/
GNU General Public License v3.0
1.06k stars 292 forks source link

generic_image reader returns data as float64 for PNG images #2897

Open strandgren opened 2 weeks ago

strandgren commented 2 weeks ago

Describe the bug The generic_image reader returns the image data as float64 for PNG images. This leads to corrupt images with the PNG and JPEG writers when trying to save the image back to a file again, while the geotiff writer falls over completely when trying to save the image back to a TIF file.

To Reproduce

from satpy import Scene

infiles = ['input.jpg', 'input.png']
outfiles =  ['output.jpg', 'output.png']
for infile, outfile in zip(infiles, outfiles):
    scn = Scene(filenames=[infile], reader='generic_image')
    scn.load(['image'])
    print(scn['image'].dtype)
    scn.save_dataset('image', outfile, enhance=False, fill_value=0)

Expected behavior

uint8
uint8

and one copy each of the input images.

Actual results

uint8
float64

and a corrupt output.png image as shown in the screenshot below. image

Environment Info:

Additional context When trying to save input.png as output.tif the geotiff writer fails with the following traceback:

Traceback (most recent call last):
  File "/tcenas/home/strandgren/git/eum/py/mtgi_imagery_stream/te_generic_reader.py", line 9, in <module>
    scn.save_dataset('image', outfile, enhance=False, fill_value=0)
  File "/tcenas/proj/optcalimg/miniconda3/envs/pysat/lib/python3.10/site-packages/satpy/scene.py", line 1234, in save_dataset
    return writer.save_dataset(self[dataset_id],
  File "/tcenas/proj/optcalimg/miniconda3/envs/pysat/lib/python3.10/site-packages/satpy/writers/__init__.py", line 885, in save_dataset
    return self.save_image(img, filename=filename, compute=compute, fill_value=fill_value, **kwargs)
  File "/tcenas/proj/optcalimg/miniconda3/envs/pysat/lib/python3.10/site-packages/satpy/writers/geotiff.py", line 263, in save_image
    raise ValueError("Image must be in 'L' mode for floating "
ValueError: Image must be in 'L' mode for floating point geotiff saving
djhoese commented 2 weeks ago

If you run gdalinfo input.png, what do you get?

djhoese commented 2 weeks ago

Ah it looks like this is intentional for fill/mask handling:

https://github.com/pytroll/satpy/blob/7261b14efa794ee7a229a0ca8c008a179e07d4b2/satpy/readers/generic_image.py#L138-L157

CC @ch-k @mraspaud

djhoese commented 2 weeks ago

See https://github.com/pytroll/satpy/pull/1560

strandgren commented 2 weeks ago

If you run gdalinfo input.png, what do you get?

!$ gdalinfo input.png
Driver: PNG/Portable Network Graphics
Files: input.png
Size is 11136, 11136
Image Structure Metadata:
INTERLEAVE=PIXEL
Corner Coordinates:
Upper Left  (    0.0,    0.0)
Lower Left  (    0.0,11136.0)
Upper Right (11136.0,    0.0)
Lower Right (11136.0,11136.0)
Center      ( 5568.0, 5568.0)
Band 1 Block=11136x1 Type=Byte, ColorInterp=Red
Mask Flags: PER_DATASET ALPHA
Band 2 Block=11136x1 Type=Byte, ColorInterp=Green
Mask Flags: PER_DATASET ALPHA
Band 3 Block=11136x1 Type=Byte, ColorInterp=Blue
Mask Flags: PER_DATASET ALPHA
Band 4 Block=11136x1 Type=Byte, ColorInterp=Alpha

I see, so then this is expected. However, is there a way to avoid broken output when saving to PNG and failing writing when trying to save to tif? I.e. can I provide some argument to save.datasets?

For my application it's easy enough to change the data type to uint8 before saving the image, but if there is a dedicated solution while writing that would be the cleaner option.

djhoese commented 2 weeks ago

I don't think so. I kind of feel like my preference would be for an option to generic_image to not auto-mask loaded data based on alpha or other TIFF properties. Put another way, if I gave you an image, give me the image data. @ch-k, @pnuu, and @mraspaud What do you think?

pnuu commented 1 week ago

What happens if you set enhance=True, does the saving work? The data might be re-stretched, but I hope the saving will then work.

I think having the kwarg @djhoese suggested above to keep the dtype intact for the input image makes a lot of sense.

I'm not sure why the image date were converted to float64 when the docstring specifically says float32.

strandgren commented 1 week ago

What happens if you set enhance=True, does the saving work? The data might be re-stretched, but I hope the saving will then work.

I think having the kwarg @djhoese suggested above to keep the dtype intact for the input image makes a lot of sense.

I'm not sure why the image date were converted to float64 when the docstring specifically says float32.

Yes, with enhance=True it works, but indeed with the caveat that the image is re-stretched, which I wont to avoid for my application (which is to load static JPG/PNG images, assign them a known area definition and then resample/crop to a new area and save the image back to a static image file).

pnuu commented 1 week ago

The next workaround to test: Create $SATPY_CONFIG_PATH/enhancements/images.yaml with

enhancements:
  default:
    operations: []

to override the default enhancement in generic.yaml only for sensor: images and then try with enhance=True.