google / jpegli

BSD 3-Clause "New" or "Revised" License
75 stars 8 forks source link

Potential of jpegli to optimize existing JPEG files #10

Open wwinniww opened 2 months ago

wwinniww commented 2 months ago

I'd like to know if jpegli has potential to optimize the size of already existing JPEG files without loosing quality visual wise. (created by libjpeg or mozjpeg)

Galaxy4594 commented 1 month ago

I don't think there is much to be gained from rewriting jpegtran from scratch imo. There are plenty other of forks/flavors out there if you want better lossless compression. I personally use jpegoptim or ECT.

EDIT: Rereading your issue, I realized it was a question of whether jpegli had the capability "to optimize jpeg files" rather than a demand for a new feature (lossless jpeg recompression). Sorry for the mix-up, I was tired at work when I wrote this.

To answer your question of "optimizing jpeg files without visually losing quality". Yes, cjpegli can process jpeg files without losing perceivable quality using the distance setting. A higher number means that the jpeg is more distorted/different from the original. The default of 1 is a good starting point since it is the cutoff where most people can't perceivably see a difference from the original. I'm more sensitive to jpeg compression so I choose a distance of 0.8. As @xiota said, its up to personal preference.

If you don't want generation loss, then no, jpegli can't make mathematically lossless jpegs (not even at distance 0). You can refer to my first statement, using jpegtran or jxl results in an image that is identical to the original while taking up less space.

xiota commented 1 month ago

cjpegli supports JPEG input. Use your favorite quality setting. Metadata needs to be copied back with another tool. This is useful to reduce bandwidth when absolute image quality doesn't matter. This is not advisable for processing images for archiving. Use lossless transcode to jxl for that.

Usage: cjpegli INPUT OUTPUT [OPTIONS...]
 INPUT
    the input can be JXL, PPM, PNM, PFM, PAM, PGX, PNG, APNG, GIF, JPEG, EXR
 OUTPUT
    the compressed JPEG output file

(Despite what GitHub thinks, I have not contributed to libjpegli. Once upon a time, I had some PRs to libjxl. That code has probably been excised from this repo.)

wwinniww commented 1 month ago

Thanks guys for your answers. One thing that I find odd is that I use the pre-compiled windows version of cjpegli provided in the latest JPEG XL zip package. The version doesn't show JPEG as valid input format. The version at my hand shows PNG, APNG, GIF, PPM, PFM and PGX as vaild formats. But cjpeg does indead accept JPG files as valid input format, but there are, as at least for me, a few shortcomings. cjepgli seems to ignore the chroma subsampling format and the scanning mode of the existing JPG file. Furthermore cjpegli does use a pre-defined fixed quality setting. So I ran into the situation that a JPEG file with the setting sequentional scan, Y420 subsampling und quality setting 85 is optimized to progressive scan, YUV444 and quality 90. The result is a bigger JPG file than the original one. While the quality setting and the change from sequentional scan to progressive might be debatable, but what's the point of a change from YUV420 to YUV444? Is there any improvement?

Anyway, metadata is lost by this way of optimizing a existing JPG file too. In the present form cjpegli isn't usable for me for optimizing existing JPG files.

Yeah, there is JPEG XL, but I want to stick with good old JPG files, at least for now.

Galaxy4594 commented 1 month ago

What's wrong with jpegxl? For archiving/storing photos it's great. The only setback is web delivery since most browsers don't support it yet.

cjepgli seems to ignore the chroma subsampling format and the scanning mode of the existing JPG file

This is also a limitation of libjpeg/mozjpeg as well. Libjpeg/mozjpeg defaults to 4:4:4 subsampling at qualities ≥ 90, 4:2:2 subsampling at qualities 80-89, and everything lower than that is 4:2:0. That was quite a shock to me since at the time I just assumed it was all 4:4:4 and you had to manually set 4:2:0. Nevertheless I agree, it would be a nice feature to have.

Furthermore cjpegli does use a pre-defined fixed quality setting.

It's not fixed like a typical quality setting, there is a quality slider but it's mapped to distance (e.g., quality 90 corresponds to distance 1), and it doesn't function the same as libjpeg. The difference with jpegli lies in how quality is tied to a measured distortion of the image. This distortion is measured using butteraugli, a metric representing human vision. This method is superior because ‘quality’ is subjective and varies depending on the image.

For example, in libjpeg, some images look fine at quality 75 while others look awful at quality 90 because it treats all images the same. Jpegli's goal is to achieve a target quality specific to each image. So, at distance 3, all images will appear low quality, while at distance 2, they’ll be medium quality, and so on. This adaptive approach eliminates the need for manual adjustments of the quality silder, I just set a target quality and let it do it's magic. It's an underrated feature of jpegli.

So I ran into the situation that a JPEG file with the setting sequentional scan, Y420 subsampling und quality setting 85 is optimized to progressive scan, YUV444 and quality 90. The result is a bigger JPG file than the original one.

This is a non issue, this "problem" appears in all lossy formats. I often hear similar sentiments with video, "I re-encoded a video at a higher quality setting than it was recorded. Why it larger than original?" It's a rookie mistake, trust me, I've been there .

The default distance of 1 is considered to be "high quality." This differs from libjpeg/mozjpeg which defaults to a quality of 75 (which I would consider "medium quality"). You encoded that same image with a higher quality than the original jpeg, of course it's going to be larger! The only thing you did was add generation loss. The phenomenon can happen with any lossy image/video format.

If there is one concession I can make, maybe the YUV 4:4:4 subsampling is bloating the image size, try again with the flag --chroma_subsampling=420 and see if that makes a difference.

but what's the point of a change from YUV420 to YUV444?

That's just the default, you can change it however to suit your needs.

I don't seem to understand your use case. If you are adamant that jpg is the only way to store images, then just use jpegtran, it also preserves metadata and it doesn't further degrade the image. If you are talking about web delivery and you don't want to use AVIF, I would recommend --chroma_subsampling=420 and -d 2, this outputs images of acceptable quality while being small in file size.

xiota commented 1 month ago

jpegli is pretty new. Depending on how actively it's developed, the issues you're having may be resolved in the future. Consider opening separate issues focused on isolated problems that could be implemented separately. Maybe don't open too many all at once, to avoid flooding developers. May also be beneficial to wait a while for them to complete housekeeping from switching to a new repo.

For now, you could take a look at cjpegli -v -v --help and write a script that behaves as you desire.

wwinniww commented 1 month ago

Hello. There is nothing wrong with JPEG XL, but it doesn't fit my purpose. I guess I have to explain my specifc use case for jpegli, to make it more understandable.

I share photos of family parties with family members. I send photos of the parties to members who don't have time or cannot participate at the parties via e-mail. But there are some restrictions. I have to take into account that some of my family members have slow internet connection speed or there are size limitations of e-mails. A few of them are no computer expert, so the photos should be viewable without any additional software on their devices, a condition that excludes the use of JPEG XL for now.

But I have a workable solution to optimize my photos. The solution I had in mind is more a general approach to it, but it's more like a nice to have solution. I know the specific parameters of the photos I want to optimize, so I can specity them at the command line of cjpegli.

wwinniww commented 1 month ago

For now, you could take a look at cjpegli -v -v --help and write a script that behaves as you desire.

I've tried that, but it makes no difference. The version of cjpegli I have at hand doesn't show jpg in the help text But to me it's more a minor cosmetic isue, because I know cjpegli does support jpeg as input file format.

ghnp5 commented 4 days ago

@xiota

cjpegli supports JPEG input.

Usage: cjpegli INPUT OUTPUT [OPTIONS...]
 INPUT
    the input can be JXL, PPM, PNM, PFM, PAM, PGX, PNG, APNG, GIF, JPEG, EXR
 OUTPUT
    the compressed JPEG output file

Strange... if I run cjpegli, this is what it gives me:

INPUT
    the input can be JXL, PPM, PNM, PFM, PAM, PGX, PNG, APNG
 OUTPUT
    the compressed JPEG output file

No JPEG listed.

And if I try to use a ".jpg" as input, I get this:

Failed to decode input image /path/to/image.jpg

I was now wondering why wouldn't cjpegli support JPG as input, which seems counterintuitive. And then I found this Issue thread.

Is this because of the way I built, or has cjpegli input support changed over time?

This is how I'm building, on an Alpine in docker:

cd /usr/local/src
git clone https://github.com/google/jpegli.git --recursive --shallow-submodules
cd jpegli
./deps.sh
./ci.sh release
./ci.sh test
mv build/tools/cjpegli /usr/local/bin/
chmod +x /usr/local/bin/cjpegli
ghnp5 commented 4 days ago

Alright... looks like I had a few dependencies missing...

Once I added these:

jpeg-dev \
libjpeg-turbo-dev \
brotli-static \
brotli-libs \
brotli-dev \
openexr-dev \

I now see the full list:

 INPUT
    the input can be JXL, PPM, PNM, PFM, PAM, PGX, PNG, APNG, JPEG, EXR
 OUTPUT
    the compressed JPEG output file