2

I have this site I am making for my school and I need to optimize it, like a lot.

So I decided to serve all my images compressed and in next-gen formats like jpeg-2000 and webp. Using Pillow, this is what I have so far:

class Bulletin(models.Model):
     banner = models.ImageField(upload_to='banner/', blank=True)

     def save(self, *args, **kwargs):
        super().save()
        if self.banner:
            thumbnail = Image.open(self.banner.path)
            resized = thumbnail.resize((1280, 620))
            resized.save(self.banner.path, quality=60)

So I think that this compresses the image (plz tell me if I made mistake with code above).

So now I want to be able to save this image in multiple formats I want the one uploaded image to be saved in these formats:

  • webp
  • jpeg2000
  • jpeg

I am thinking of creating more fields on my model banner, like the field banner_webp, and then I would just convert it to webp and during save I would save the converted image to that field.

The problem is that I don’t know how to convert image using Pillow, or how to do what I am asking. Thanks for help.

  • Sorry if my bounty isn't clear, I want to have an answer on how to save an image in multiple formats using django. Thanks. – John Doe May 29 '21 at 13:07
  • I think you did not read the Pillow documentation. It has both the formats you mentioned (JPEG 2000 and WebP), although with some additional dependencies. https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html# – mostlycryptic May 29 '21 at 13:35
  • @Aaqib Bashir, I worded my question wrong. I want to CONVERT the users uploaded image to webp, jpeg2000, and jpeg. I know Pillow has both formats capability. – John Doe May 29 '21 at 14:46
  • "Conversion" with pillow should simply be loading and saving with a new format. Take a look at [`Image.save()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.save): Calling it with a filename that has "jpeg" or "webp" as extension should suffice. Is this what you want? Then I'll add it as an answer. – He3lixxx May 29 '21 at 20:55

1 Answers1

2

John already found a working solution. I'd suggest the following changes to that:

  • Assuming a Bulletin has some more fields that might get changed without the banner changing: You don't need to re-encode your images on every save() of a Bulletin. If you put the encoding into its own function, you can specifically call that function in the upload view, if that is the only place in your code that modifies this value. There are possibilities to detect in save() whether the field was changed, so this could also be an option.
  • If you keep the custom save() method: You should pass on the arguments in the super call: super().save(*args, **kwargs)
  • You don't need to remove the old extension: You can name your images "whatever.jpg.webp". It won't confuse the computer, and I think this name makes it very clear that it is a reencoding of the original file. Also, this allows you to keep the original file as-is, in case you one day decide that you want your images encoded with some other parameters, e.g. with a different quality setting.
  • Having a uniform accessor to the reencoded image file path allows you to prevent some typo-related bugs.
class Bulletin(models.Model):
    BANNER_REENCODE_FORMATS = ["jpg", "jp2", "webp"]
    banner = models.ImageField(upload_to='banner/', blank=True)

    def reencoded_banner_path(self, file_format="jpg"):
        assert file_format in self.BANNER_REENCODE_FORMATS
        return self.banner.path + "." + file_format

    def reencode_banner(self):
        image = Image.open(self.banner.path).convert("RGB").resize((1280, 620))
        for extension in self.BANNER_REENCODE_FORMATS:
            image.save(self.reencoded_banner_path(extension), quality=60)

with the following template tag:

@register.filter(name='reencoded_banner_path')
@stringfilter
def reencoded_banner_path(bulletin, format):
    return bulletin.reencoded_banner_path(format)

you could use this in your template:

{{ bulletin|reencoded_banner_path:"webp" }}

Note that you probably want to have some renaming logic on image uploads so that stuff doesn't break if users upload two files with the same filename.

He3lixxx
  • 3,263
  • 1
  • 12
  • 31