Search code examples
pythonmatplotlibpython-imaging-librarypng

Is a PNG file size supposed to remain static when using Python PIL 'quality' parameter while saving?


To the best of my knowledge I have my code ( below ) functioning as anticipated. I open an image and then iteratively generate copies of that image with varying settings upon saving it, specifically modifying the quality and optimization values at that step. My goal is to determine what's the best parameters for my use case but upon running the script I found results that I didn't understand.

Regardless of what I set "Quality" to , the size of the resulting PNG was the same. The PNG file size changed when switching between "optimize" ( true/false ) but not when quality was adjusted.

Is this anticipated or have I made an error in my code below?

'''I want to see how small I can go with the best picture quality'''

from PIL import Image
import matplotlib.pyplot as plt
from pathlib import Path
import os


def make_lists(result_images):
    '''Returns a dict of lists containing image size values'''
    plot_dict = {}
    for ext, val in result_images.items():
        for opt, var in val.items():
            plot_dict[f'{ext}-{opt}'] = var
    return plot_dict


def draw_result(plot_dict):
    '''Draws resulting image data as a graph'''
    colors = ['green', 'red', 'blue', 'orange']
    c = 0
    for key, val in plot_dict.items():
        plt.plot(val, color=colors[c], label=key)
        c += 1
    plt.legend()
    plt.show()


def generate_images():
    '''Generates N images with adjustments to optimization and quality with each output
    leaving the size the same each time'''
    im_path = 'full.jpg'
    out_path = 'out'

    if not os.path.exists(out_path):
        os.makedirs(out_path)

    im = Image.open(im_path)
    new_im = im.resize(im.size, Image.ANTIALIAS)

    qualities = [100, 75, 50, 25]
    optimize = [True, False]
    extens = ['png', 'jpg']

    result_images = {}

    for ext in extens:
        result_images[ext] = {}
        for opt in optimize:
            result_images[ext][opt] = []
            for q in qualities:
                fpath = f'{out_path}/{q}{opt}.{ext}'
                new_im.save(fpath, optimize=opt, quality=q)
                footprint = Path(fpath).stat().st_size
                result_images[ext][opt].append(footprint)
    return result_images


result_images = generate_images()

plot_dict = make_lists(result_images)

draw_result(plot_dict)

Solution

  • The documentation for the save method mentions this:

    Keyword options can be used to provide additional instructions to the writer. If a writer doesn’t recognise an option, it is silently ignored. The available options are described in the image format documentation for each writer.

    And indeed, the documentation for the PNG writer doesn't mention a quality option, so it's a value that's being ignored in the save call.