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

Refine NewJxlExportParams and discuss on Effort/Tier #416

Merged
merged 1 commit into from
Mar 18, 2024

Conversation

n0vad3v
Copy link
Contributor

@n0vad3v n0vad3v commented Mar 17, 2024

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
Copy link

Coverage Status

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

@tonimelisma
Copy link
Collaborator

Thanks @n0vad3v - looks good to me!

@tonimelisma tonimelisma merged commit e348555 into davidbyttow:master Mar 18, 2024
1 check passed
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

Successfully merging this pull request may close these issues.

3 participants