Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PNG (and probably AVIF) encoding with compression too slow #2248

Closed
thestarmaker opened this issue May 30, 2024 · 6 comments
Closed

PNG (and probably AVIF) encoding with compression too slow #2248

thestarmaker opened this issue May 30, 2024 · 6 comments

Comments

@thestarmaker
Copy link

thestarmaker commented May 30, 2024

Hi there, it's a bit of a saga, but the gist is that it looks like compression is taking way too long on MacOS (over a minute on M1 for PNG).

Check this code:

        use libheif_rs::{
            RgbChroma, ColorSpace, HeifContext, LibHeif
        };
        use std::io::BufWriter;
        use std::fs::File;
        

        let lib_heif = LibHeif::new();
        let ctx = HeifContext::read_from_file("/Users/<YOUR USERNAME>/dev/scrap/IMG_6974.HEIC").unwrap();
        let handle = ctx.primary_image_handle().unwrap();

        // Decode the image
        let image = lib_heif.decode(
            &handle,
            ColorSpace::Rgb(RgbChroma::Rgba),
            None,
        ).unwrap();


        let p = image.planes();
        let img : ImageBuffer<Rgba<u8>, _> = ImageBuffer::from_raw(image.width(), image.height(), p.interleaved.unwrap().data).unwrap();

        let file = File::create("/Users/<YOUR USERNAME>/dev/scrap/fractal.png").unwrap();
        let ref mut buff = BufWriter::new(file);
        let encoder = PngEncoder::new_with_quality(buff, CompressionType::Best, FilterType::default());
        encoder.write_image(img.as_bytes(), img.width(), img.height(), ExtendedColorType::Rgba8).unwrap();

This uses 'libheif-rs = "1.0.2"' for reading HEIC image (attached) and then tries to write it as PNG. Reading is quick enough, writing takes much longer. If I change the compression to CompressionType::Fast, it becomes much quicker. The resulting image is much larger than the original HEIC (~10Mb for CompressionType::Best, and ~26Mb for CompressionType::Fast).

The reason I have actually started looking at this is because I want AVIF rather than PNG, but writing AVIF was taking absolutely forever and I came to using PNGs as an investigatory step. It seems from readme that AVIFs are "lossy-only", therefore I am assuming there is some common processing that is related to compression slowing things down.

This is happening on MacOS, M1 chip, Rust 1.77.2, stable.

P.S.
sorry about the zip, even Github does not like HEIC

IMG_6974.HEIC.zip

@kornelski
Copy link
Contributor

The slowness of AVIF is expected. It's a complex codec. The underlying library rav1e has speed settings, but the faster it goes the less advantage it has over other formats (you can make it as fast as webp, but files will have similar filesize/quality as webp too).

PNG file sizes are not comparable to HEIC or AVIF. PNG is lossless, and the other ones are lossy. These are entirely different requirements. It's especially terrible when you take a lossy file and encode it in PNG, because all the cut corners and distortions that made the file small before are now alien noise that PNG is told to preserve exactly, and that costs even more to encode. As a result you get lots of data to encode, a huge PNG, and that's going to be slow too.

@kornelski
Copy link
Contributor

If you want to compress images well, tolerate the slowness of AVIF.

If you have lossy images and you want to save them quickly, use JPEG.

@thestarmaker
Copy link
Author

@kornelski Thank you for taking a look at this. Let's ignore the PNG for now, the only reason I started lookign at PNG compression levels was because I could not get AVIF encoded within reasonable amount of time.

If we update the example I've originally provided to encode the image as AVIF:

        let mut bw = BufWriter::new(File::create("/Users/<YOUR USERNAME>/dev/scrap/avified.avif").unwrap());
        img.write_to(&mut bw, image::ImageFormat::Avif).unwrap();

then encoding takes 274 seconds on my machine. The produced image is small, 732Kb worth of AVIF vs 2.1Mb in the original HEIC. The encodign has also held 10 CPU cores saturated for the entire encoding period:

PID   COMMAND      %CPU  TIME     #TH    #WQ  #PORT MEM    PURG   CMPR PGRP PPID STATE
1212  base-fa4a273 970.8 04:08.68 12/10  0    23    248M+  0B     0B   1208 1208 running
894   rustrover    3.1   02:10.85 118    2    584   3083M  129M   0B   894  1    sleeping

Is this really how expensive AVIF encoding is?

@kornelski
Copy link
Contributor

How large is the image in pixels?

Are you sure you're building in release mode?

@fintelia
Copy link
Contributor

fintelia commented Jun 2, 2024

You likely also want to enable the nasm feature, though you'll need to make sure you have nasm installed first. There's more details in the rav1e documentation

@thestarmaker
Copy link
Author

cool, looks like it was another case of courage and stupidity. I was experimenting with image-rs from unit tests which are compiled with optlevel 0. I have just tried the example above in release mode, encoding the example image (which is 3024 × 4032 by the way) now takes 17 seconds instead of 274 seconds like it used to.

Thanks everyone involved for looking at this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants