14

I'm trying to resize & reduce quality of image before upload in project. Here's what I tried,

def save(self):
    im = Image.open(self.image)
    output = BytesIO()
    im = im.resize(240, 240)
    im.save(output, format='JPEG', quality=95)
    output.seek(0)
    self.image = InMemoryUploadedFile(output, 'ImageField', "%s.jpg" % self.image.name.split('.')[0], 'image/jpeg', sys.getsizeof(output), None)
    super(Model, self).save()

It's working fine if I upload a jpg image but if I upload a png or any other image type, it's not working it's raising errors like cannot write mode RGBA as JPEG & cannot write mode P as JPEG etc.

How can we fix that? Thank You!

  • @Teemu Sir, whenever I'm uploading a `jpg` image it's working fine but if I'm uploading any other type of image it's raising an error. How can we change image type to `jpg` so that it can work properly. I'm new here so I've mistakenly added `JavaScript` & `PHP`, I've removed them :) –  Jan 11 '18 at 12:33
  • I'm not familiar with python, but "cannot write mode RGBA as JPEG" sounds reasonable only with jpg ..? – Teemu Jan 11 '18 at 12:36
  • @Teemu Sir, this error is occurring when I'm uploading a `png` image –  Jan 11 '18 at 12:37

2 Answers2

30

If your image.mode is "P" or "RGBA" and you want to convert it to jpeg then you need to first convert the image.mode because the previous modes aren't supported for jpeg

if im.mode in ("RGBA", "P"):
    im = im.convert("RGB")

https://github.com/python-pillow/Pillow/issues/2609

timop
  • 842
  • 6
  • 7
4

Summary timop and 2:

  • backgroud

    • JPG not support alpha = transparency
    • RGBA, P has alpha = transparency
      • RGBA= Red Green Blue Alpha
  • result

    • cannot write mode RGBA as JPEG
    • cannot write mode P as JPEG
  • solution

    • before save to JPG, discard alpha = transparency
      • such as: convert Image to RGB
    • then save to JPG
  • your code

    if im.mode == "JPEG":
        im.save(output, format='JPEG', quality=95)
    elif im.mode in ["RGBA", "P"]:
        im = im.convert("RGB")
        im.save(output, format='JPEG', quality=95)
    
  • More for you:

about resize & reduce quality of image, I have implement a function, for you (and others) to refer:

from PIL import Image, ImageDraw
cfgDefaultImageResample = Image.BICUBIC # Image.LANCZOS

def resizeImage(inputImage,
                newSize,
                resample=cfgDefaultImageResample,
                outputFormat=None,
                outputImageFile=None
                ):
    """
        resize input image
        resize normally means become smaller, reduce size
    :param inputImage: image file object(fp) / filename / binary bytes
    :param newSize: (width, height)
    :param resample: PIL.Image.NEAREST, PIL.Image.BILINEAR, PIL.Image.BICUBIC, or PIL.Image.LANCZOS
        https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.thumbnail
    :param outputFormat: PNG/JPEG/BMP/GIF/TIFF/WebP/..., more refer:
        https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
        if input image is filename with suffix, can omit this -> will infer from filename suffix
    :param outputImageFile: output image file filename
    :return:
        input image file filename: output resized image to outputImageFile
        input image binary bytes: resized image binary bytes
    """
    openableImage = None
    if isinstance(inputImage, str):
        openableImage = inputImage
    elif CommonUtils.isFileObject(inputImage):
        openableImage = inputImage
    elif isinstance(inputImage, bytes):
        inputImageLen = len(inputImage)
        openableImage = io.BytesIO(inputImage)

    if openableImage:
        imageFile = Image.open(openableImage)
    elif isinstance(inputImage, Image.Image):
        imageFile = inputImage
    # <PIL.PngImagePlugin.PngImageFile image mode=RGBA size=3543x3543 at 0x1065F7A20>
    imageFile.thumbnail(newSize, resample)
    if outputImageFile:
        # save to file
        imageFile.save(outputImageFile)
        imageFile.close()
    else:
        # save and return binary byte
        imageOutput = io.BytesIO()
        # imageFile.save(imageOutput)
        outputImageFormat = None
        if outputFormat:
            outputImageFormat = outputFormat
        elif imageFile.format:
            outputImageFormat = imageFile.format
        imageFile.save(imageOutput, outputImageFormat)
        imageFile.close()
        compressedImageBytes = imageOutput.getvalue()
        compressedImageLen = len(compressedImageBytes)
        compressRatio = float(compressedImageLen)/float(inputImageLen)
        print("%s -> %s, resize ratio: %d%%" % (inputImageLen, compressedImageLen, int(compressRatio * 100)))
        return compressedImageBytes

latest code can found here:

https://github.com/crifan/crifanLibPython/blob/master/crifanLib/crifanMultimedia.py

crifan
  • 12,947
  • 1
  • 71
  • 56