fdintino / pillow-avif-plugin

A pillow plugin that adds avif support via libavif
BSD 2-Clause "Simplified" License
90 stars 13 forks source link

AVIF size too big #35

Closed RaphaelVRossi closed 11 months ago

RaphaelVRossi commented 1 year ago

Hey @fdintino, I'm using pillow_avif lib and after some tests the size of the generated AVIF files are bigger than other format files.

Do you know how can I increase the quality without losing file compression?

Bellow, you will find some tests comparing other ways to convert AVIF files.

To create all images used for this test, run the following commands:

wget https://live.staticflickr.com/1379/540719764_cddd076c3b_o_d.jpg
wget https://live.staticflickr.com/1149/540424829_2e6236161f_o_d.jpg
wget https://live.staticflickr.com/1223/540424821_912db14c86_o_d.jpg
wget https://live.staticflickr.com/231/512656223_213d76330f_o_d.jpg
wget https://live.staticflickr.com/213/512656219_ce1c963ea0_o_d.jpg

avifenc --min 0 --max 63 --speed 10 -y 420 -a end-usage=q -a cq-level=18 -a tune=ssim 540719764_cddd076c3b_o_d.jpg 540719764_cddd076c3b_o_d_libavif.avif
avifenc --min 0 --max 63 --speed 10 -y 420 -a end-usage=q -a cq-level=18 -a tune=ssim 540424829_2e6236161f_o_d.jpg 540424829_2e6236161f_o_d_libavif.avif
avifenc --min 0 --max 63 --speed 10 -y 420 -a end-usage=q -a cq-level=18 -a tune=ssim 540424821_912db14c86_o_d.jpg 540424821_912db14c86_o_d_libavif.avif
avifenc --min 0 --max 63 --speed 10 -y 420 -a end-usage=q -a cq-level=18 -a tune=ssim 512656223_213d76330f_o_d.jpg 512656223_213d76330f_o_d_libavif.avif
avifenc --min 0 --max 63 --speed 10 -y 420 -a end-usage=q -a cq-level=18 -a tune=ssim 512656219_ce1c963ea0_o_d.jpg 512656219_ce1c963ea0_o_d_libavif.avif

magick 540719764_cddd076c3b_o_d.{jpg,avif}
magick 540424829_2e6236161f_o_d.{jpg,avif}
magick 540424821_912db14c86_o_d.{jpg,avif}
magick 512656223_213d76330f_o_d.{jpg,avif}
magick 512656219_ce1c963ea0_o_d.{jpg,avif}

Run this script to compare all images

from io import BytesIO

from PIL import Image
from SSIM_PIL import compare_ssim
import pillow_avif

IMAGES = [
    "540719764_cddd076c3b_o_d",
    "540424829_2e6236161f_o_d",
    "540424821_912db14c86_o_d",
    "512656223_213d76330f_o_d",
    "512656219_ce1c963ea0_o_d",
]

def main():
    for image_name in IMAGES:
        with open(f"{image_name}.jpg", "rb") as buffer, open(f"{image_name}.avif", "rb") as magick_buffer, open(f"{image_name}_libavif.avif", "rb") as libavif_buffer:
            orig_image = Image.open(buffer)

            avif_buff = BytesIO()
            orig_image.save(avif_buff, "AVIF")

            avif_image = Image.open(avif_buff)

            webp_buff = BytesIO()
            orig_image.save(webp_buff, "WEBP")

            webp_image = Image.open(webp_buff)

            magick_image = Image.open(magick_buffer)
            libavif_image = Image.open(libavif_buffer)

            print("--------------------------------")

            print("original size    ", buffer.tell())
            print("webp size        ", webp_buff.tell())
            print("avif size        ", avif_buff.tell())
            print("libavif size     ", libavif_buffer.tell())
            print("magick size      ", magick_buffer.tell())

            print("SSIM webp        ", compare_ssim(orig_image, webp_image))
            print("SSIM pillow_avif ", compare_ssim(orig_image, avif_image))
            print("SSIM libavif     ", compare_ssim(orig_image, libavif_image))
            print("SSIM magick      ", compare_ssim(orig_image, magick_image))

if __name__ == "__main__":
    main()

Result:

--------------------------------
original size     627850
webp size         66582
avif size         67604
libavif size      61120
magick size       31890
SSIM webp         0.9486322981857666
SSIM pillow_avif  0.956722795048049
SSIM libavif      0.9532628629321842
SSIM magick       0.9431758917273692
--------------------------------
original size     650898
webp size         42304
avif size         41588
libavif size      39573
magick size       23312
SSIM webp         0.9368172311565952
SSIM pillow_avif  0.9420409271228755
SSIM libavif      0.9403307321056051
SSIM magick       0.9344050306077084
--------------------------------
original size     202878
webp size         125918
avif size         141235
libavif size      130351
magick size       67927
SSIM webp         0.9562758092233508
SSIM pillow_avif  0.9745698329186758
SSIM libavif      0.9630233447898499
SSIM magick       0.9437101105895831
--------------------------------
original size     735832
webp size         142010
avif size         146454
libavif size      130914
magick size       70457
SSIM webp         0.9605508960021887
SSIM pillow_avif  0.972782090002052
SSIM libavif      0.9682353517962792
SSIM magick       0.9472827910724667
--------------------------------
original size     676188
webp size         53342
avif size         48452
libavif size      49211
magick size       25116
SSIM webp         0.9434370564874662
SSIM pillow_avif  0.9486846647258985
SSIM libavif      0.946304458529975
SSIM magick       0.9407932141513629

Pictures by Marcelo Jorge Vieira licensed under CC-BY.

fdintino commented 12 months ago

If you're satisfied with the lower imagemagick SSIM values, pass a lower quality keyword argument to the save method. The file sizes and SSIMs look comparable if you pass in quality values between 40 and 50. Using the SVT-AV1 codec (by calling im.save("filename.avif", codec="svt", quality=45)) ought to get you even better compression.

RaphaelVRossi commented 11 months ago

Thanks @fdintino 🚀

RaphaelVRossi commented 11 months ago

hey @fdintino ,

How to set log level for svt codec?

Every time I save a image using svt codec this log appears

Svt[info]: -------------------------------------------
Svt[info]: SVT [version]:       SVT-AV1 Encoder Lib v1.7.0
Svt[info]: SVT [build]  :       GCC 10.2.1 20210130 (Red Hat 10.2.1-11)  64 bit
Svt[info]: LIB Build date: Oct 13 2023 02:11:49
Svt[info]: -------------------------------------------
Svt[warn]: If you are using tiles with the intent of increasing the decoder speed, please also consider using --fast-decode 1, especially if the intended decoder is running with limited multi-threading capabilities.
Svt[info]: Number of logical cores available: 4
Svt[info]: Number of PPCS 74
Svt[info]: [asm level on system : up to sse4_2]
Svt[info]: [asm level selected : up to sse4_2]
Svt[info]: -------------------------------------------
Svt[info]: SVT [config]: main profile   tier (auto)     level (auto)
Svt[info]: SVT [config]: width / height / fps numerator / fps denominator : 1000 / 672 / 60000 / 1000
Svt[info]: SVT [config]: bit-depth / color format                         : 8 / YUV420
Svt[info]: SVT [config]: preset / tune / pred struct                      : 8 / PSNR / random access
Svt[info]: SVT [config]: gop size / mini-gop size / key-frame type        : 321 / 32 / key frame
Svt[info]: SVT [config]: BRC mode / rate factor                           : CRF / 28
Svt[info]: -------------------------------------------
Svt[warn]: Failed to set thread priority
Svt[warn]: Failed to set thread priority
Svt[warn]: Failed to set thread priority

I'm currently using your plugin inside Thumbor

RaphaelVRossi commented 11 months ago

So, digging thought SVT source code, I found a env var SVT_LOG that control log level.

After export this env the log was suppressed.