Better quality does not always require a larger file

A request for better image quality can mean blur, incorrect orientation, unreadable text or excessive compression. Identify the actual defect before choosing a tool. Enlarging a small picture alone cannot restore real detail that was never captured. For a website, useful quality means that the subject remains clear at its displayed size while the asset avoids unnecessary transfer costs. Evaluate those objectives together rather than treating quality and file size as unrelated tasks.

The helper applies camera orientation metadata before reducing dimensions. It never enlarges the source and saves a new WebP file using the selected quality setting without overwriting an existing destination. Animated images and oversized pixel dimensions are rejected. This is an editorial processing tool for selected input files. Receiving arbitrary uploads from visitors additionally requires request-size controls, authorization and an explicit validation boundary rather than assuming that an image converter is a complete upload service.

Control orientation, size and encoding separately

An encoder's quality number is not a universal percentage of retained detail. The example creates three settings and records byte size and average absolute color-channel differences. That difference is not a complete human-perception metric; text, edges and important textures deserve visual inspection. Real photographs, color profiles and the eventual display size can influence the decision. Avoid presenting a low numeric error as proof that all meaningful details remain equally readable to a visitor.

Save the four files and install Pillow in a project virtual environment. Run python -m unittest test_optimize.py, then python make_sample.py and python compare.py. The comparison produces three output files from a reproducible synthetic fixture. The fixture supports repeatable checks rather than representing a measured result for your production photographs. Repeating these commands in a directory containing previous outputs raises an error instead of silently replacing the earlier evidence or source material.

Tests exercise no-upscaling behavior, preservation of an existing destination and camera orientation. For real media, inspect both full-resolution details and the intended website size. If the original photograph is out of focus, locate a better source rather than expecting resizing to repair it. Brand-critical or print-oriented colors may need explicit profile management. The example's simple mode conversion does not guarantee perfect agreement among every embedded profile and viewing application.

Complete executable files

The four files are complete. The article provides prerequisites, command order and verification; code is neither shortened nor hidden.

1. Complete optimize.py

Save this entire file beside the others using the exact filename.

from pathlib import Path
from PIL import Image, ImageOps


def optimize(source, destination, max_width=1200, quality=85):
    source, destination = Path(source), Path(destination)
    if not 1 <= max_width <= 4000 or not 60 <= quality <= 95 or destination.suffix.lower() != '.webp':
        raise ValueError('Use width 1..4000, quality 60..95 and a WebP destination')
    with Image.open(source) as original:
        if getattr(original, 'is_animated', False) or original.width * original.height > 20_000_000:
            raise ValueError('Use a single image of at most 20 million pixels')
        image = ImageOps.exif_transpose(original)
        image.thumbnail((max_width, 4000), Image.Resampling.LANCZOS)
        image = image.convert('RGBA' if 'A' in image.getbands() else 'RGB')
        with destination.open('xb') as output:
            image.save(output, format='WEBP', quality=quality, method=6)
    return {'width': image.width, 'height': image.height, 'bytes': destination.stat().st_size}

The execution section explains this file’s command, expected outcome and limits.

2. Complete make_sample.py

Save this entire file beside the others using the exact filename.

from pathlib import Path
from PIL import Image, ImageDraw


def create_sample(path):
    image = Image.new('RGB', (1600, 900), '#eae9e3')
    draw = ImageDraw.Draw(image)
    for x in range(0, 1600, 8):
        draw.line((x, 0, x, 900), fill=(x % 256, 80, 130), width=3)
    draw.rectangle((200, 250, 1400, 650), outline='#15342b', width=5)
    with Path(path).open('xb') as output:
        image.save(output, format='PNG')


if __name__ == '__main__': create_sample('sample.png')

The execution section explains this file’s command, expected outcome and limits.

3. Complete compare.py

Save this entire file beside the others using the exact filename.

from pathlib import Path
from PIL import Image, ImageChops, ImageOps, ImageStat
from optimize import optimize

if __name__ == '__main__':
    source = Path('sample.png')
    for quality in (75, 85, 95):
        target = Path(f'sample-q{quality}.webp')
        result = optimize(source, target, max_width=960, quality=quality)
        with Image.open(source) as original, Image.open(target) as encoded:
            reference = ImageOps.exif_transpose(original).convert('RGB')
            reference.thumbnail(encoded.size, Image.Resampling.LANCZOS)
            error = ImageStat.Stat(ImageChops.difference(reference, encoded.convert('RGB'))).mean
        print({'quality': quality, **result, 'meanAbsoluteChannelError': [round(x, 2) for x in error]})

The execution section explains this file’s command, expected outcome and limits.

4. Complete test_optimize.py

Save this entire file beside the others using the exact filename.

from pathlib import Path
import tempfile
import unittest
from PIL import Image
from optimize import optimize


class ImageTests(unittest.TestCase):
    def test_no_upscaling_or_overwriting(self):
        with tempfile.TemporaryDirectory() as directory:
            source, target = Path(directory) / 'in.png', Path(directory) / 'out.webp'
            Image.new('RGB', (100, 50), 'red').save(source)
            self.assertEqual(optimize(source, target)['width'], 100)
            with self.assertRaises(FileExistsError): optimize(source, target)

    def test_orientation(self):
        with tempfile.TemporaryDirectory() as directory:
            source, target = Path(directory) / 'in.jpg', Path(directory) / 'out.webp'
            image = Image.new('RGB', (50, 100), 'blue')
            exif = Image.Exif(); exif[274] = 6; image.save(source, exif=exif)
            result = optimize(source, target)
            self.assertEqual((result['width'], result['height']), (100, 50))


if __name__ == '__main__': unittest.main()

The execution section explains this file’s command, expected outcome and limits.

Run a comparison and choose a useful output

After choosing an output, configure responsive candidates, intrinsic dimensions and main-image loading priority as appropriate. None of these choices alone guarantees a search ranking, but they can support a clearer page and a better visitor experience. Keep the original, process a copy and compare the same subject at its actual display size. Prefer an acceptable visual result with measured bytes over reflexively selecting the largest quality number available in an export dialog.

Implementation checklist

  • Seek a better original when detail was never captured.
  • Apply orientation before resizing.
  • Compare bytes and visual readability together.
  • Preserve originals and existing outputs.

References

Editorial guidance: Liyan Knowledge.