Skip to content

Bug when optimizing JPEG #7604

@LewisCowlesMotive

Description

@LewisCowlesMotive

What did you do?

I ran the attached code to resize, and then save an image from URL; I checked using a jupyter notebook and at various stages the image seems fine, so it seems to be saving code, with optimize=True which causes the issue.

What did you expect to happen?

Image resized, without artifacts

13246

What actually happened?

Artifacts in the image. Unsure why.

13246

What are your OS, Python and Pillow versions?

  • OS: MacOS
  • Python: 3.11.6
  • Pillow: 10.1.0
Code to download an image, process it as 128x128 and save it

13246

import base64
import io
from pathlib import Path
from typing import Tuple

import requests
from PIL import Image
EXIF_ORIENTATION_BYTE_OFFSET = 0x0112

url = "http://localhost:8080/13246.png"
s = requests.Session()
response = s.get(url, timeout=10)
image_file = io.BytesIO(response.content)

def make_white_copy(image: Image, background_color: Tuple[int, int, int]=(255, 255, 255)) -> Image:
    """Ensure we get correct background when cropping image."""
    width = image.width
    height = image.height
    is_landscape = width > height
    is_portrait = height > width
    x_pos = 0
    y_pos = 0
    if width != height:
        if is_landscape:
            desired_width = width
            desired_height = int(height * (width / height))
            x_pos = 0
            y_pos = int((desired_width - height) / 2)
        elif is_portrait:
            desired_width = int(width * (height / width))
            desired_height = height
            x_pos = int((desired_height - width) / 2)
            y_pos = 0

    new_img = Image.new("RGB", (desired_width, desired_height), background_color)
    new_img.paste(image, (x_pos, y_pos))
    return new_img


def transpose_by_exif(image: Image) -> Image:
    """Use exif data if present to perform action on image."""
    orientation = image.getexif().get(EXIF_ORIENTATION_BYTE_OFFSET)
    if orientation is not None and orientation > 1:
        transposition_action = {
            2: Image.FLIP_LEFT_RIGHT,
            3: Image.ROTATE_180,
            4: Image.FLIP_TOP_BOTTOM,
            5: Image.TRANSPOSE,
            6: Image.ROTATE_270,
            7: Image.TRANSVERSE,
            8: Image.ROTATE_90,
        }.get(orientation)
        image = image.transpose(transposition_action)
    return image


def resize(image: Image) -> Image:
    """Resize Image to dimensions."""
    size = (128, 128)
    image.thumbnail(size)
    return image

saved_image = None
img = Image.open(image_file)
managed_image = resize(make_white_copy(img))
with Path.open("/Users/lewiscowles/Desktop/13246.jpg", "w") as out:
    managed_image.save(
        out,
        "JPEG",
        optimize=True,
        quality=100,
        jfif_unit=1,
        dpi=(96, 96),
        jfif_density=(96, 96),
    )

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions