Skip to content
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

«OSError: cannot write mode RGBA as JPEG» when get_thumbnail for PNG in mode=P #564

Open
stopdesign opened this issue Sep 16, 2018 · 10 comments

Comments

@stopdesign
Copy link

What did I do?

Tried to get thumbnail (JPEG without transparency) for PNG image in mode=P (indexed colours?).
(It works good for PNG with mode=RGBA but not mode=P)

What did I expect to happen?

Saving thumbnail without any exceptions, flattened alpha-channel into solid background of some colour (eg THUMBNAIL_PADDING_COLOR, some specific colour from settings or 'white').

Test image

5847e9aacef1014c0b5e4828
https://user-images.githubusercontent.com/244666/45594613-a5aba780-b9a6-11e8-9321-da6710f9fb4f.png

What actually happened?

>>> i = Image.open('forbes.png')
>>> i
<PIL.PngImagePlugin.PngImageFile image mode=P size=400x400 at 0x7FE650252CC0>

>>> get_thumbnail(i, '100x106', crop='center', quality=99, format='PNG')
<sorl.thumbnail.images.ImageFile at 0x7fe6501d6390>

>>> get_thumbnail(i, '100x100', crop='center', quality=99, format='JPEG')
OSError: cannot write mode RGBA as JPEG

Versions

Python == 3.6.5
Pillow == 5.2.0 (or any versions >= 4.2.0)
sorl-thumbnail == 12.4.1

Related issue

python-pillow/Pillow#2609

How to fix it?

You need to make a background image with certain colour and merge source image into background:

    def _colorspace(self, image, colorspace, format):
        if colorspace == 'RGB':
            # Pillow JPEG doesn't allow RGBA anymore. It was converted to RGB before.
            if image.mode == 'RGBA' and format != 'JPEG':
                return image  # RGBA is just RGB + Alpha
            if image.mode == 'LA' or (image.mode == 'P' and 'transparency' in image.info):
                if format == 'JPEG':
                    newimage = Image.new('RGB', image.size, '#eebbaa')
                    mask = image.convert('RGBA').split()[-1]
                    newimage.paste(image.convert('RGBA'), (0, 0), mask)
                else:
                    newimage = image.convert('RGBA')
                    transparency = image.info.get('transparency')
                    if transparency is not None:
                        mask = image.convert('RGBA').split()[-1]
                        newimage.putalpha(mask)
                return newimage
            return image.convert('RGB')
        if colorspace == 'GRAY':
            return image.convert('L')
        return image

Correct result

cor

@coler-j
Copy link

coler-j commented Dec 31, 2018

Is there going to be a PR for this?

@VdeJong
Copy link

VdeJong commented Feb 14, 2019

I have the same issue when saving an image through django rest framework. Where should I put this piece of code @stopdesign ?

@troef
Copy link

troef commented Feb 23, 2019

This comment is not related to the issue but might help people like me looking for a solution. If you don't mind having PNG format thumbnails you can use the following setting

THUMBNAIL_PRESERVE_FORMAT = True

This saves PNG images as PNG thumbnails and thus bypasses the RGBA problem completely.

@TomaszKot11
Copy link

Still relevant :)

@nsomaru
Copy link

nsomaru commented Feb 7, 2020

I'm curious as to why sorl worked fine before and now it doesn't in this case. Maybe it was better to just throw a warning?

@nsomaru
Copy link

nsomaru commented Feb 7, 2020

So part of the issue for me is in the fact that sorl uses the file extension to determine the output format instead of actually reading the headers of the file like Pillow does to determine format. That means that the workaround THUMBNAIL_PRESERVE_FORMAT = True does not work for me when a user has misnamed a jpg file as a png file. I assume that any patch to fix this would be engine dependent, I'd love a pointer as to how to start on this to get this done.

Should this be an independent issue?

@sowinski
Copy link

Same problem here. Will be there a fix for this?

@sowinski
Copy link

Any update on this? :)

@benjaoming
Copy link
Contributor

benjaoming commented Apr 21, 2022

My current work-around:

# ImageField from model
image_field = image_model.image
img = Image.open(image_field.file)
fill_color = "#ffffff"
if img.mode in ("RGBA", "LA"):
    # Necessary because of "Bad Transparency Mask" issues with LA mode, this makes errors disappear, untested if LA conversion to RGBA results in a desirable transparency mask
    if img.mode == "LA":
        img = img.convert("RGBA")
    background = Image.new(img.mode[:-1], img.size, fill_color)
    background.paste(img, img.split()[-1])
    img = background
    thumbnail = get_thumbnail(img, "800x800", quality=90, format="JPEG")
else:
    thumbnail = get_thumbnail(img, "800x800", quality=90)

As you can see, it's possible to put this behavior directly in get_thumbnail, if some alpha_replace is provided. I think that the behavior could follow automatically:

By suppling a kwarg alpha_replace to get_thumbnail(img, "800x800", alpha_replace="#ffffff"), SORL will automatically detect and replace alpha channels

This way - even if for whatever reason the developer wants to do it on PNG, it will happen. If the kwarg is not supplied, then we can still fail if asked to convert a PNG with an alpha channel to JPEG (which IMO is correct behavior).

@pfcodes
Copy link

pfcodes commented Oct 4, 2022

Bump

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

No branches or pull requests

9 participants