davidbyttow / govips

A lightning fast image processing and resizing library for Go
MIT License
1.23k stars 196 forks source link

Refine `NewJxlExportParams` and discuss on `Effort`/`Tier` #416

Closed n0vad3v closed 5 months ago

n0vad3v commented 5 months ago

Currently NewJxlExportParams returns the following struct:

JxlExportParams{
        Quality:  75,
        Lossless: false,
        Effort:   7,
    }

However, using this default JxlExportParams, the export time will be multiple times the magnitude of the time using vips/cjxl.

From cjxl --help:

 -e EFFORT, --effort=EFFORT
    Encoder effort setting. Range: 1 .. 9.
     Default: 7. Higher number is more effort (slower).
 -d maxError, --distance=maxError
    Max. butteraugli distance, lower = higher quality.
    0.0 = mathematically lossless. Default for already-lossy input (JPEG/GIF).
    1.0 = visually lossless. Default for other input.
    Recommended range: 0.5 .. 3.0. Allowed range: 0.0 ... 25.0.
    Mutually exclusive with --quality.

 --compress_boxes=0|1
    Disable/enable Brotli compression for metadata boxes (not provided = default, 0 = disable, 1 = enable).
 --brotli_effort=B_EFFORT
    Brotli effort setting. Range: 0 .. 11.
    Default: 9. Higher number is more effort (slower).
 --faster_decoding=0|1|2|3|4
    Favour higher decoding speed. 0 = default, higher values give higher speed at the expense of quality

From libvips:

/**
 * vips_jxlsave: (method)
 * @in: image to save
 * @filename: file to write to
 * @...: %NULL-terminated list of optional named arguments
 *
 * Optional arguments:
 *
 * * @tier: %gint, decode speed tier
 * * @distance: %gdouble, maximum encoding error
 * * @effort: %gint, encoding effort
 * * @lossless: %gboolean, enables lossless compression
 * * @Q: %gint, quality setting
 *
 * Write a VIPS image to a file in JPEG-XL format.
 *
 * The JPEG-XL loader and saver are experimental features and may change
 * in future libvips versions.
 *
 * @tier sets the overall decode speed the encoder will target. Minimum is 0
 * (highest quality), and maximum is 4 (lowest quality). Default is 0.
 *
 * @distance sets the target maximum encoding error. Minimum is 0
 * (highest quality), and maximum is 15 (lowest quality). Default is 1.0
 * (visually lossless).
 *
 * As a convenience, you can also use @Q to set @distance. @Q uses
 * approximately the same scale as regular JPEG.
 *
 * Set @lossless to enable lossless compression.
 *
 * Returns: 0 on success, -1 on error.
 */

We can see that default distance used by libvips is 1.0(rather than zero-value in Go of 0.0), so this PR adds Distance 1.0 to NewJxlExportParams.

And there seems a mismatch that tier option is not available in cjxl.

The only tier I can find in libjxl are these:

enum class SpeedTier {
  // Try multiple combinations of Glacier flags for modular mode. Otherwise
  // like kGlacier.
  kTectonicPlate = -1,
  // Learn a global tree in Modular mode.
  kGlacier = 0,
  // Turns on FindBestQuantizationHQ loop. Equivalent to "guetzli" mode.
  kTortoise = 1,
  // Turns on FindBestQuantization butteraugli loop.
  kKitten = 2,
  // Turns on dots, patches, and spline detection by default, as well as full
  // context clustering. Default.
  kSquirrel = 3,
  // Turns on error diffusion and full AC strategy heuristics. Equivalent to
  // "fast" mode.
  kWombat = 4,
  // Turns on gaborish by default, non-default cmap, initial quant field.
  kHare = 5,
  // Turns on simple heuristics for AC strategy, quant field, and clustering;
  // also enables coefficient reordering.
  kCheetah = 6,
  // Turns off most encoder features. Does context clustering.
  // Modular: uses fixed tree with Weighted predictor.
  kFalcon = 7,
  // Currently fastest possible setting for VarDCT.
  // Modular: uses fixed tree with Gradient predictor.
  kThunder = 8,
  // VarDCT: same as kThunder.
  // Modular: no tree, Gradient predictor, fast histograms
  kLightning = 9
};

Tests

I have a JPG photo shot by Sony A7M3 with size of 11MB.

Using vips took 3.8s

time vips copy 1.jpg output.jxl --vips-progress
vips temp-4: 6000 x 4000 pixels, 16 threads, 6000 x 16 tiles, 384 lines in buffer
vips temp-4: done in 0.181s
vips copy 1.jpg output.jxl --vips-progress 13.55s user 2.20s system 413% cpu 3.804 total

Using cjxl 0.8.2 took 1.7s

time cjxl --num_threads=0 1.jpg 1.jxl
JPEG XL encoder v0.8.2 [AVX2,SSE4,SSSE3,SSE2]
Note: Implicit-default for JPEG is lossless-transcoding. To silence this message, set --lossless_jpeg=(1|0).
Read JPEG image with 11942483 bytes.
Encoding [Container | JPEG, lossless transcode, effort: 7 | JPEG reconstruction data], 
Compressed to 9922997 bytes including container 
cjxl --num_threads=0 1.jpg 1.jxl  1.33s user 0.44s system 99% cpu 1.769 total

Using govips with NewJxlExportParams took 85.89s

package main

import (
    "fmt"
    "os"
    "time"

    "github.com/davidbyttow/govips/v2/vips"
)

func main() {
    vips.LoggingSettings(nil, vips.LogLevelError)
    vips.Startup(nil)
    defer vips.Shutdown()

    img, err := vips.NewImageFromFile("1.jpg")
    if err != nil {
        panic(err)
    }
    startTime := time.Now()
    imageBytes, _, _ := img.ExportJxl(vips.NewJxlExportParams())
    _ = os.WriteFile("input_jxl.jxl", imageBytes, 0644)

    fmt.Println("JXL Distance took", time.Since(startTime))
    fmt.Println("JXL Image size: ", float64(len(imageBytes))/float64(1024)/float64(1024), "MB")

}
JXL with,took:  1m25.897941276s
JXL Image size:  16.545248985290527 MB

Using govips with Distance 1.0 took 8.28s

package main

import (
    "fmt"
    "os"
    "time"

    "github.com/davidbyttow/govips/v2/vips"
)

func main() {
    vips.LoggingSettings(nil, vips.LogLevelError)
    vips.Startup(nil)
    defer vips.Shutdown()

    img, err := vips.NewImageFromFile("1.jpg")
    if err != nil {
        panic(err)
    }

    startTime := time.Now()
    imageBytes, _, _ := img.ExportJxl(&vips.JxlExportParams{
        Lossless: false,
        Distance: 1.0,
        Quality:  75,
        Effort:   7,
    })
    _ = os.WriteFile("input_jxl.jxl", imageBytes, 0644)

    fmt.Println("JXL with,took: ", time.Since(startTime))
    fmt.Println("JXL Image size: ", float64(len(imageBytes))/float64(1024)/float64(1024), "MB")
}
JXL with,took:  8.285116573s
JXL Image size:  2.0766067504882812 MB

However, this is still much slower than vips or cjxl, maybe we should change default Effort or Tier to 4? I have some benchmark here:

Effort: 0 Tier: 0 Time: 7.833351495s Size: 2.0766067504882812 MB
Effort: 1 Tier: 0 Time: 952.246453ms Size: 2.4485464096069336 MB
Effort: 2 Tier: 0 Time: 968.036485ms Size: 2.4484996795654297 MB
Effort: 3 Tier: 0 Time: 997.907243ms Size: 2.205158233642578 MB
Effort: 4 Tier: 0 Time: 1.265137705s Size: 2.247774124145508 MB
Effort: 5 Tier: 0 Time: 3.380930959s Size: 1.9037599563598633 MB
Effort: 6 Tier: 0 Time: 4.476601287s Size: 1.9887895584106445 MB
Effort: 7 Tier: 0 Time: 8.051399046s Size: 2.0766067504882812 MB
Effort: 8 Tier: 0 Time: 42.102255629s Size: 2.04257869720459 MB

Effort: 0 Tier: 1 Time: 8.486599268s Size: 2.1964111328125 MB
Effort: 1 Tier: 1 Time: 893.716482ms Size: 2.502410888671875 MB
Effort: 2 Tier: 1 Time: 902.096165ms Size: 2.502408981323242 MB
Effort: 3 Tier: 1 Time: 928.118852ms Size: 2.333489418029785 MB
Effort: 4 Tier: 1 Time: 1.145933061s Size: 2.3801727294921875 MB
Effort: 5 Tier: 1 Time: 3.905123538s Size: 2.019237518310547 MB
Effort: 6 Tier: 1 Time: 5.034431819s Size: 2.1082916259765625 MB
Effort: 7 Tier: 1 Time: 8.502604482s Size: 2.1964111328125 MB
Effort: 8 Tier: 1 Time: 41.773230794s Size: 2.184206962585449 MB

Effort: 0 Tier: 2 Time: 8.088012048s Size: 2.4210472106933594 MB
Effort: 1 Tier: 2 Time: 929.987956ms Size: 2.5151195526123047 MB
Effort: 2 Tier: 2 Time: 944.436608ms Size: 2.5151195526123047 MB
Effort: 3 Tier: 2 Time: 963.507318ms Size: 2.3461999893188477 MB
Effort: 4 Tier: 2 Time: 985.514523ms Size: 2.5740604400634766 MB
Effort: 5 Tier: 2 Time: 3.518071661s Size: 2.258063316345215 MB
Effort: 6 Tier: 2 Time: 4.377649705s Size: 2.3371477127075195 MB
Effort: 7 Tier: 2 Time: 7.74622447s Size: 2.4210472106933594 MB
Effort: 8 Tier: 2 Time: 40.615410806s Size: 2.515275001525879 MB

Effort: 0 Tier: 3 Time: 7.768423391s Size: 2.4210472106933594 MB
Effort: 1 Tier: 3 Time: 912.423943ms Size: 2.5151195526123047 MB
Effort: 2 Tier: 3 Time: 924.362133ms Size: 2.5151195526123047 MB
Effort: 3 Tier: 3 Time: 917.347857ms Size: 2.3461999893188477 MB
Effort: 4 Tier: 3 Time: 1.054520617s Size: 2.5740604400634766 MB
Effort: 5 Tier: 3 Time: 3.189269061s Size: 2.258063316345215 MB
Effort: 6 Tier: 3 Time: 4.275487427s Size: 2.3371477127075195 MB
Effort: 7 Tier: 3 Time: 7.841357036s Size: 2.4210472106933594 MB
Effort: 8 Tier: 3 Time: 40.376279915s Size: 2.515275001525879 MB

Effort: 0 Tier: 4 Time: 7.31356593s Size: 2.4838247299194336 MB
Effort: 1 Tier: 4 Time: 917.8056ms Size: 2.5151195526123047 MB
Effort: 2 Tier: 4 Time: 969.955736ms Size: 2.5151195526123047 MB
Effort: 3 Tier: 4 Time: 909.595841ms Size: 2.3461999893188477 MB
Effort: 4 Tier: 4 Time: 1.043404674s Size: 2.5740604400634766 MB
Effort: 5 Tier: 4 Time: 2.90211247s Size: 2.3764801025390625 MB
Effort: 6 Tier: 4 Time: 4.203880566s Size: 2.4217491149902344 MB
Effort: 7 Tier: 4 Time: 8.185684618s Size: 2.4838247299194336 MB
Effort: 8 Tier: 4 Time: 41.297486808s Size: 2.5310497283935547 MB
coveralls commented 5 months ago

Coverage Status

coverage: 73.206% (-0.03%) from 73.232% when pulling 3336e6def156d2997e79d565d80836e41a69fb0a on webp-sh:master into 143c4c119b385f38e85f0402b9bb1d161c6ccb5d on davidbyttow:master.

tonimelisma commented 5 months ago

Thanks @n0vad3v - looks good to me!