Skip to content

Saving PNG images with PIL is 4 times slower than saving them with OpenCV #5986

@apacha

Description

@apacha

What did you do?

I want to save an image to disk and noticed a severe performance bottleneck for the part of my code that used PIL for saving images compared to a similar part in my codebase that uses OpenCV to save the images.

What did you expect to happen?

I expected both methods to be somewhat similar in performance.

What actually happened?

PIL was at least four times slower than converting the PIL.Image into an numpy array and storing the array using cv2.imwrite.

What are your OS, Python and Pillow versions?

  • OS: MacOS 12.1
  • Python: 3.9
  • Pillow: 9.0.0

Here is the benchmark code that I used:

import time
import cv2
import numpy
from PIL import Image
from tqdm import tqdm
from PIL.ImageDraw import ImageDraw

if __name__ == '__main__':
    image = Image.new("RGB", (4000, 2800))
    image_draw = ImageDraw(image)
    image_draw.rectangle((10, 20, 60, 120), fill=(230, 140, 25))
    trials = 50

    t1 = time.time()
    for i in tqdm(range(trials)):
        image.save("tmp1.png")
    t2 = time.time()
    print(f"Total time for PIL: {t2 - t1}s ")

    t1 = time.time()
    for i in tqdm(range(trials)):
        image_array = numpy.array(image)
        image_array = cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR)
        cv2.imwrite("tmp2.png", image_array)
    t2 = time.time()
    print(f"Total time for OpenCV: {t2 - t1}s ")

    img1 = cv2.imread("tmp1.png")
    img2 = cv2.imread("tmp2.png")
    print(f"Images are equal: {numpy.all(img1 == img2)}")

which produced

100%|██████████| 50/50 [00:26<00:00,  1.91it/s]
Total time for PIL: 26.21s 
100%|██████████| 50/50 [00:06<00:00,  8.00it/s]
Total time for OpenCV: 6.24s 
Images are equal: True

The produced images are slightly different in file-size so potentially there is a more sophisticated compression being used.

Here are the two (black) images I obtained
tmp1.png (PIL-image, 33KB)
tmp2.png (OpenCV-image, 38KB)

My questions are:

  • Why is PIL so much slower?
  • Is there some way how I can speed up PIL to match the performance of OpenCV in this scenario (other than converting to a numpy array and using OpenCV), e.g., by providing extra parameters to the save method?

Interestingly, if I switch from PNG to JPG, the results are flipped and PIL is faster than OpenCV:

# saving as "tmp1.jpg" and "tmp2.jpg" instead
100%|██████████| 50/50 [00:04<00:00, 11.46it/s]
Total time for PIL: 4.37s 
100%|██████████| 50/50 [00:08<00:00,  6.11it/s]
Total time for OpenCV: 8.17s 

could this be a problem in the PNG encoding library?

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