satpy icon indicating copy to clipboard operation
satpy copied to clipboard

saving composites as .jpg with generated alpha channel fails in satpy/trollimage....

Open peters77 opened this issue 4 years ago • 11 comments

Describe the bug Saving a composite with an added alpha channel by a compositor (MaskingCompositor or CloudCompositor) fails if .jpg was used as file format.

To Reproduce I try to save hrv_severe_storms_masked composite as .jpg with this line and a given fill_value: new_scene.save_dataset(composite, imgdir + 'MSG-4-euro-raw.jpg', overlay = {'coast_dir': '/home/cpeters/software/', 'overlays': {'coasts': coast, 'borders': borders, 'rivers': rivers, 'points': points}}, decorate = {'decorate': deco}, fill_value=0)

Debug output:

Actual results `[DEBUG: 2021-03-28 11:31:03 : satpy.writers.simple_image] Saving to image: /mnt/nfs/rumo/web/satpy/test/MSG-4-euro-raw.jpg Traceback (most recent call last): File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/PIL/JpegImagePlugin.py", line 611, in _save rawmode = RAWMODE[im.mode] KeyError: 'RGBA'

The above exception was the direct cause of the following exception:

Traceback (most recent call last): File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/trollimage/xrimage.py", line 299, in delayed_pil_save img.save(*args, **kwargs) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/PIL/Image.py", line 2151, in save save_handler(self, fp, filename) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/PIL/JpegImagePlugin.py", line 613, in _save raise OSError(f"cannot write mode {im.mode} as JPEG") from e OSError: cannot write mode RGBA as JPEG

The above exception was the direct cause of the following exception:

Traceback (most recent call last): File "make_msg4_static_ir_clouds_without_platform_boarders_etc.py", line 457, in 'overlays': {'coasts': coast, 'borders': borders, 'rivers': rivers, 'points': points}}, decorate = {'decorate': deco}, fill_value=0) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/satpy/scene.py", line 995, in save_dataset compute=compute, **save_kwargs) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/satpy/writers/init.py", line 811, in save_dataset return self.save_image(img, filename=filename, compute=compute, fill_value=fill_value, **kwargs) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/satpy/writers/simple_image.py", line 67, in save_image return img.save(filename, compute=compute, **kwargs) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/trollimage/xrimage.py", line 423, in save compute=compute, **format_kwargs) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/trollimage/xrimage.py", line 617, in pil_save return delay.compute() File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/dask/base.py", line 167, in compute (result,) = compute(self, traverse=False, **kwargs) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/dask/base.py", line 452, in compute results = schedule(dsk, keys, **kwargs) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/dask/threaded.py", line 84, in get **kwargs File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/dask/local.py", line 486, in get_async raise_exception(exc, tb) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/dask/local.py", line 316, in reraise raise exc File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/dask/local.py", line 222, in execute_task result = _execute_task(task, data) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/dask/core.py", line 121, in _execute_task return func(*(_execute_task(a, cache) for a in args)) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/dask/utils.py", line 29, in apply return func(*args, **kwargs) File "/home/cpeters/anaconda2/envs/satpy/lib/python3.7/site-packages/trollimage/xrimage.py", line 305, in delayed_pil_save raise OSError(new_msg) from e OSError: Image mode not supported for this format. Specify fill_value=0 to set invalid values to black.`

Environment Info:

  • OS: Linux
  • Satpy Version: 0.26.0

peters77 avatar Mar 28 '21 09:03 peters77

Anybody have an idea what the expected result should be? Should the alpha band be removed? Should it be used to mask the non-alpha bands with the fill value in some way? If this hadn't worked before I think my gut reaction would have been that it shouldn't be allowed to work now.

djhoese avatar Mar 28 '21 12:03 djhoese

This worked in the past. I don't understand why I was advised OSError: Image mode not supported for this format. Specify fill_value=0 to set invalid values to black. to set it to zero but I already did it? I got black backgrounds if I set it in the past. But maybe somone who is more advanced could answer the question. If it will not be allowed any more to save RGBA to jpg I would vote for a more clear error message maybe?

peters77 avatar Mar 28 '21 13:03 peters77

@peters77 Yes of course. My main question is what do you expect the output to be? Your input data has an alpha channel, but the JPEG format doesn't support alpha channels. The error message is due to JPEG not supporting an alpha channel. Normally trollimage adds an alpha channel to make invalid pixels transparent, but specifying fill_value=0 would stop this alpha band from being created. In your case, the alpha channel was provided with your data so trollimage doesn't know what to do. It assumes that since you provided the alpha channel that you want to keep it. So should trollimage remove the alpha channel and leave the other non-alpha channels as-is? Or should trollimage use the fill_value you provide and the alpha channel to mask out values in the non-alpha bands? Or should we just update the error message to say that this dataset can't be saved to jpeg?

djhoese avatar Mar 28 '21 15:03 djhoese

@djhoese In my case I just want to save it as .jpg and mabe remove the alpha channel!? Here is an example. Only the right lower corner is affected in my case. But let's see what @trygveas has to say to it? (see @pnuu https://pytroll.slack.com/archives/C0LNH7LMB/p1616922805267000). MSG-4-euro-raw

peters77 avatar Mar 28 '21 18:03 peters77

Thanks for discussing this.

What I tried was to make a grayscale image( one satellite band) with an alpha channel. If I add fill_value=0 to save_dataset I expect the resulting geotiff to be without the alpha channel. But the saved geotiff have the alpha channel.

I'm not sure it this is a bug or a feature, or if this worked in the past.

I do like this:

from satpy import Scene, find_files_and_readers
from satpy.utils import debug_on
debug_on()

base_dir = '<my base dir>'
files = find_files_and_readers(base_dir=base_dir)
data = Scene(filenames=files)
products = ['nwc_geo_ct_masked_ir']

data.load(products)
resampled = data.resample('euro4', radius_of_influence=50000)
resampled.save_dataset(products[0], fill_value=0)

This is a bit tricky as you need both the SEVIRI and SAFNWC CloudType product.

TAlonglong avatar Mar 29 '21 07:03 TAlonglong

Common to both these use-cases is that the compositor (CloudCompositor for @peters77 and MaskingCompositor for @TAlonglong) explicitly adds the alpha layer. For CloudCompositor the fill_value=0 should definitely work, but MaskingCompositor might be more complicated as it could have several different levels of transparency.

pnuu avatar Mar 29 '21 08:03 pnuu

I expect the resulting geotiff to be without the alpha channel. But the saved geotiff have the alpha channel.

@TAlonglong I'm guessing the first "geotiff" should say "jpeg"? So different results depending on the format? Right now the format and how it is handled is up to PIL (for any non-geotiff formats). We'd have to have a list of formats that support alpha bands and ones that don't to be able to handle this properly. Also are you saying that fill_value=0 for a geotiff still includes the alpha?

djhoese avatar Mar 29 '21 11:03 djhoese

@TAlonglong Any updates on the above?

djhoese avatar Apr 17 '21 15:04 djhoese

Anybody have a feeling about what the final point of action should be to resolve this issue? If fill_value is specified and there is an alpha band, then remove the alpha band? Or maybe there are cases where an alpha band and a fill value makes sense?

djhoese avatar Nov 16 '21 16:11 djhoese

I vote for removing the alpha band.

mraspaud avatar Nov 16 '21 19:11 mraspaud

Any further thoughts on this? I came across the same error today.

simonrp84 avatar Mar 05 '22 16:03 simonrp84