34
\$\begingroup\$

For years, I've believed that recompressing JPEG files multiple times would gradually degrade its quality until they are an unrecognizable mess, the way making photocopies of photocopies does. This intuitively makes sense because JPEG is a lossy format. There are also other Q&As that claim this is so:

However, I've also read that recompressing JPEGs at the same quality level will not degrade the image quality. This runs counter to the gradual degradation that is described elsewhere.

What technically happens when a JPEG is recompressed? What is being lost and how? Will the image really transform into the snowy mess that used to appear on television? What about those videos showing images that fall apart after being recompressed multiple times?

(Please don't merely handwave and appeal to the general concept of lossiness.)

(This question, and the answers it has attracted so far, focus on the technical factors (specific settings and image manipulations) that cause or prevent image degradation when a JPEG file is recompressed multiple times.)

\$\endgroup\$
9
  • 1
    \$\begingroup\$ Possible duplicate of What image quality is lost when re-saving a JPEG image in MS Paint? \$\endgroup\$
    – Michael C
    Commented Jun 27, 2018 at 20:25
  • \$\begingroup\$ On the other hand: Does simply opening and closing a JPEG file decrease image quality? \$\endgroup\$
    – Michael C
    Commented Jun 27, 2018 at 20:26
  • \$\begingroup\$ Relevant. \$\endgroup\$
    – user541686
    Commented Jun 28, 2018 at 9:40
  • 2
    \$\begingroup\$ @MonkeyZeus Some (small) amount of image data is lost by rounding error at quality 100. Recompressing at the same setting (such as 80) does not result in progressive data loss. That is the "common knowledge" this Q&A is intended to address. \$\endgroup\$
    – xiota
    Commented Jun 28, 2018 at 18:37
  • 1
    \$\begingroup\$ @MonkeyZeus The values like "100" and "80" (or "10, 11, 12" in Photoshop) are arbitrary — 100% is not lossless. \$\endgroup\$
    – mattdm
    Commented Jun 28, 2018 at 21:58

4 Answers 4

36
\$\begingroup\$

Almost all image quality losses occur the first time an image is compressed as JPEG. Regardless of how many times a JPEG is recompressed with the same settings, generational losses are limited to rounding error.

  • MCU boundaries remain are intact (8x8 blocks).

  • Chroma subsampling is disabled.

  • Constant DQT (same quality setting).

However, rounding errors may be large for each iteration that the above criteria are not met, and it is prudent to keep backups of all original files.


The JPEG compression algorithm

  1. Convert colorspace. If desired, downsample color information (chroma subsampling) (Lossy). If not downsampled, loss of information is the result of rounding error.

  2. Segmentation. Divide each channel into 8x8 blocks (MCU = Minimal Coding Unit). (Lossless)

    Note: If chroma subsampling is enabled, MCUs may effectively be 16x8, 8x16, or 16x16, in terms of the original image. However, the MCUs are still all 8x8 blocks.

  3. Discrete Cosine Transform (DCT) on each MCU. Loss of information is the result of rounding error.

  4. Quantization. The value in each cell of the MCU is divided by a number specified in a quantization table (DQT). Values are rounded down, many of which will become zero. This is the primary lossy portion of the algorithm.

  5. Zig-Zag Scan. Rearrange values in each MCU into a sequence of numbers following a zig-zag pattern. The zeros that occurred during quantization will be grouped together. (Lossless)

  6. DPCM = Differential Pulse Code Modulation. Convert the number sequences into a form that is easier to compress. (Lossless)

  7. RLE = Run Length Encoding. Consecutive zeros are compressed. (Lossless)

  8. Entropy/Huffman Coding. (Lossless)

Recompressing JPEGs

Note that downsampling the color channels and quantization are the only intentionally lossy steps. Setting aside rounding error for now, all other steps are lossless. Once quantization has occurred, reversing and repeating the step gives identical results. In other words, re-quantization (with the same DQT) is lossless.

In principle, it is possible to create a resampling algorithm that is lossless after the first pass. However, with the implementation in ImageMagick, colors may shift drastically before steady state is reached, as seen in ths' image.

Given optimal conditions, recompressing a JPEG with the same quality settings would result in the exact same JPEG. In other words, recompressing JPEGs is potentially lossless. In practice, recompressing JPEGs is not lossless, but subject to, and limited by, rounding error. Although rounding errors often eventually converge to zero, so that the exact same image is re-created, chroma subsampling may result in significant color changes.

Demonstration (same quality setting)

I wrote the following bash script, which uses ImageMagick to repeatedly recompress a JPEG file at a given quality setting:

#!/usr/bin/env bash
n=10001; q1=90
convert original.png -sampling-factor 4:4:4 -quality ${q1} ${n}.jpg

while true ; do
   q2=${q1}            # for variants, such as adding randomness
   convert ${n}.jpg -quality ${q2} $((n+1)).jpg
   #\rm $((n-5)).jpg   # uncomment to avoid running out of space
   n=$((n+1))

   echo -n "$q2  "
   md5sum ${n}.jpg
done

After letting it run for a few hundred iterations, I ran md5sum on the results:

d9c0d55ee5c8b5408f7e50f8ebc1010e  original.jpg

880db8f146db87d293def674c6845007  10316.jpg
880db8f146db87d293def674c6845007  10317.jpg
880db8f146db87d293def674c6845007  10318.jpg
880db8f146db87d293def674c6845007  10319.jpg
880db8f146db87d293def674c6845007  10320.jpg

We can see that, indeed, the rounding error has converged to zero, and the exact same image is being reproduced, over and over.

I have repeated this multiple times with different images and quality settings. Usually, steady state is reached, and the exact same image is reproduced over and over.

What about @mattdm's results?

I have attempted to replicate mattdm's results using Imagemagick on Ubuntu 18.04. The original was a raw conversion to TIFF in Rawtherapee, but it seems to be no longer available. In its place, I took the enlarged version and reduced it to its original size (256x256). Then I repeatedly recompressed at 75 until I got convergence. Here is the result (original, 1, n, difference):

attempt to replicate mattdm

My results are different. Without the true original, the reason for the difference is impossible to determine.

What about @ths's montage?

I recompressed the image from the upper left corner of the montage until convergence at 90. This is the result (original, 1, n, difference):

attempt to replicate ths-montage

After enabling chroma subsampling, the colors do change by the time steady state is reached.

ths-color-shift

Changing among a small number of settings

By modifying the variable q2, the quality setting can be limited to a set of evenly distributed values.

q2=$(( (RANDOM % 3)*5  + 70 ))

For a small number of setting choices, equilibrium may eventually reached, which is seen when md5 values begin recurring. It seems the larger the set, the longer it takes, and the worse the image becomes, before equilibrium can be reached.

What seems to happen at equilibrium is the DCT coefficient prior to quantization has to be divisible all (or most) of the quantum values. For example, if switching between two DQTs where DCT coefficient is divided alternately by 3 and 5, equilibrium will be reached when the DCT coefficient is divisible by 15. This explains why the drop in quality is much greater than the difference between the original settings.

Changing among a larger number of settings

Eeyore is not happy when q2 is changed like so:

q2=$(( (RANDOM % 9)  + 90 ))

To make a video, use ffmpeg:

rename 's@1@@' 1*.jpg
ffmpeg -r 30 -i %04d.jpg -c:v libx264 -crf 1 -vf fps=25 -pix_fmt yuv420p output.mp4

Watching the first 9999 iterations is almost like watching water boil. Might want to double playback speed. Here is Eeyore after 11999 iterations:

11999 iterations, random DQT

What if MCU boundaries change?

If changes occur a limited number of times, repeatedly recompressing is likely to reach steady state. If changes occur at each iteration, the image will probably degrade in a manner similar to when DQT changes.

  • This is what happens in videos that rotate an image with dimensions that are not divisible by 8.

What about editing?

The effect of recompressing after editing depends on the particular edit performed. For instance, saving at the same quality setting after reducing JPEG artifacts would reintroduce the same artifacts. However, applying a localized change, such as a healing brush, would not affect areas that were not touched.

The greatest drop in image quality occurs the first time the file is compressed at a given quality setting. Subsequently recompressing with the same setting should not introduce any change greater than rounding error. So I would expect edit-resave cycles at a given quality setting to look like any other image saved with the same quality setting (as long as MCU boundaries stay intact and chroma subsampling is disabled).

What about those videos?

Can I over-write my originals with recompressed JPEGs?

It is prudent to keep backups of all original files, but if you accidentally overwrite one, the damage is likely limited. It would also be fine to work in JPEG with chroma subsampling disabled.

JPEG cannot be used for images that use more than 8 bits per color.

\$\endgroup\$
23
  • 6
    \$\begingroup\$ the picture is quite different with load-edit-save loops, though. in this case, repeated quantization will cause degradation. \$\endgroup\$
    – ths
    Commented Jun 27, 2018 at 11:55
  • 2
    \$\begingroup\$ i just made a test with the same script as in the answer. here is a montage of every 20th image up tp 100:i.sstatic.net/xtob6.jpg that is significant. \$\endgroup\$
    – ths
    Commented Jun 27, 2018 at 13:07
  • 3
    \$\begingroup\$ ah. found the problem with my image. if you have chroma subsampling turned on, that leads to progressive degradation. \$\endgroup\$
    – ths
    Commented Jun 27, 2018 at 15:43
  • 3
    \$\begingroup\$ Found that too. So enabling chroma subsampling significantly alters the color in the image before steady state is reached. \$\endgroup\$
    – xiota
    Commented Jun 27, 2018 at 15:56
  • 2
    \$\begingroup\$ Repeated loads and saves using the exact same parameters would likely not introduce unbounded quality loss, since most input files could be loaded and re-saved without introducing additional rounding error, and files that would be affected by rounding errors would occur would likely be transformed into files that wouldn't. On the other hand, repeated loading/save cycles that alternate between compression parameters that are similar but not identical could result in rounding errors on every cycle. \$\endgroup\$
    – supercat
    Commented Jun 27, 2018 at 16:24
21
\$\begingroup\$

Recompression loss is real, especially when working with higher levels of JPEG compression.

In theory, if you re-save a JPEG files with the exact same parameters and have aligned your crop to 8×8 blocks, the degradation should be minimal. However, if you're using a high level of compression, you will see further loss, because the artifacts introduced by the initial compression are permanent changes to the image and will get recompressed too, causing more artifacts.

If you re-save with a low level of compression (high quality, like "100" in Gimp or 11 or 12 in Photoshop), any newly added artifacts will be hard to notice. It won't make the image any better, but not significantly worse. However, it will introduce changes across the whole image.

As a quick test, I used ImageMagick to recompress a JPEG image over and over at 75%. The samples below are uploaded as PNG files to avoid yet further recompression, and were doubled in size when I converted to PNG to make the effect more obvious. (The originals used in the test were not doubled.) It turns out that after eight resamplings, the effect converged on a perfectly stable result, where recompressing again results in a bit-for-bit identical file.

Here's the uncompressed original:

original, no jpeg compression

Here's the result of going to 75% JPEG:

first jpeg

And here's that resaved:

second pass

That single second save causes a large amount of additional degradation!

And here's the final converged image (8th pass):

converged jpeg

Again, colors are definitely even more off, including some false color patterns, and the blocky artifacts jump out more. The algorithm converges, but to a significantly degraded version. So, don't do that.

But here's the same thing with a 99% quality level, after 9 passes (the point where it converges so further passes are identical):

99% 9 times

Here, the difference barely registers. (I mean that literally; compare them pixel by pixel to the non-compressed version and the deviation is just very slight random noise.) So, what if I go back to that first 75% image and then resave at 99%? Well, this, (after just once):

75% once and then 99% once

Saving at high quality is definitely visibly better than resaving with the same parameters, kind of to my surprise. But, there's obvious new degradation around the pink trimming and the eyes. With the recycled version of the same settings, the JPEG artifacts are being exaggerated with each recompression. With the low resolution and low quality I've chosen, that turns out to be worse than recompressing everything differently.

On those videos: I found this one as a top Google hit. Note that it says in the description:

This is what happens if you re-encode a JPEG image many times, at random high quality settings (85 or above).

Emphasis added — this explains why there isn't any convergence, because instead of saving with the same settings, or saving at super high quality, random settings are used each time.

The second video I found says:

A JPEG image was copied and rotated a full revolution for each image. [...] (596 "rotate clockwise" actions)

So, again, something was done to keep the errors accumulating.

In any case, for practical photo editing, it's worth mentioning that saving 75% one time is much worse than resaving at 99% a million times. In my example case, the artifacts at 75% are so obvious that the further degradation is like dumping water in the ocean. If you save at a high enough level that these artifacts are not really visible, saving again with the original settings is a good strategy. Of course, if you can stick to always working from uncompressed originals, you're better off.

If for some reason you have to (or strongly prefer to) just work with JPEG, set your camera to save in the highest quality possible, even if you don't notice the difference in the initial files. See Is it worth using Pentax's Premium JPEG quality setting? for more on that — not necessarily really Pentax specific.

\$\endgroup\$
12
  • \$\begingroup\$ (1) You're saving at 75%. At that setting, loss of image quality is expected. (2) That image was selected and altered to exaggerate JPEG compression artifacts. (3) The image converges after 8 recompression rounds, after which there will be no further reduction in image quality. (4) A video of that image showing "generation loss" would have a whole lot of nothing happening after the first 1/4 second. \$\endgroup\$
    – xiota
    Commented Jun 27, 2018 at 13:13
  • 5
    \$\begingroup\$ (1)Yes. (2) "Selected" as a typical photo where one might care about this kind of thing. "Altered" only to zoom in. Note that this is only for display here — I didn't double the size of the image I was working with. (3) Yes, but in practice for editing, it's the first few rounds you might care about. (4) That's true, but it doesn't imply that converging to the worst case and staying there is useful in any way. \$\endgroup\$
    – mattdm
    Commented Jun 27, 2018 at 13:22
  • \$\begingroup\$ To replicate, take the first image and resize to 256×256 without any resampling or interpolation. \$\endgroup\$
    – mattdm
    Commented Jun 27, 2018 at 13:24
  • \$\begingroup\$ I can't see much of a difference between the images you show. But if I take the difference of a singly-recompressed and a mutliply-recompressed image and amplify it to make it visible, I get this (much more convincing) result: i.sstatic.net/57uaY.png (see my deleted answer on what was done exactly) It's more convincing because people don't have to keep staring at the image to detect minute differences. \$\endgroup\$
    – Szabolcs
    Commented Jun 27, 2018 at 18:14
  • \$\begingroup\$ The differences are pretty small. If you have a large LCD screen, the different "gamma" that results from slightly different viewing angles can make artifacts appear more prominent. \$\endgroup\$
    – xiota
    Commented Jun 28, 2018 at 4:00
5
\$\begingroup\$

Recompression does have a measurable effect on image quality and that effect is much more pronounced when changing compression rates.

As a quick check here as some SSIM values for operations performed on a test image containing a combination of line features and continuous features. I selected JPG95 because that is what I was taught to use at Ad-photo school and JPG83 because that is common among digital content providers.

  • Save Tiff image as JPG95 - .9989
  • Save Tiff image as JPG83 - .9929
  • Resave JPG95 image as JPG95 10 times - .9998
  • Resave JPG83 image as JPG83 10 times - .9993
  • Resave JPG95 as JPG83 then resave as JPG95 - .9929
  • Resave JPG95 as JPG83 then JP83 to JP92 then JPG92 to JPG86 - .9914

So the amount of structural similarity lost to resaving at the same compression 10 times is 1/10th of that lost as saving it at the quality from tiff. However, the quality loss from changing the JPG compression even once is the same as the quality lost to saving that image from Tiff to JPG.

I'll run this test a few more ways and update.

Methodology: In ImageJ:

  1. Convert Tiff RGB to grayscale 8-bit
  2. Save JPG95 and JPG83 from Tiff Original
  3. Conduct further resave operations as specified
  4. Load comparison images and use SSIM Index Plugin

NOTE: many people looking at SSIM values for the first time read them as percentages and assume the difference is small. This is not necessarily true. SSIM values should be compared relative to one another rather than considered as a variance from 1.

\$\endgroup\$
4
  • \$\begingroup\$ @xiota, I'm using an SSIM plugin for ImageJ. It is one of few SSIM implementations that allows adjustment to parameters (I set the filter width to be 8 so that it would be more likely to detect changes within the 16px JPEG blocks.) I prefer SSIM because it is more sensitive to differences in energy redistribution. A difference image can be misleading if differences cancel out or the differences are concentrated in a small area. \$\endgroup\$ Commented Jun 27, 2018 at 15:19
  • \$\begingroup\$ And to your second question, that says that the difference going from JPG95 to JPG83 to JPG95 is the same as going from Tiff to JPG83. If you want Tiff-JPG95-JPG83-JPG95, that is .9923 \$\endgroup\$ Commented Jun 27, 2018 at 15:20
  • \$\begingroup\$ Added a try with four different compressions. The loss is still greater but it's clear that the "convergence" seen over several generations of the same compression is also present when trying multiple different compressions. Still would like to try this in an App-centric workflow but that takes a bit more legwork. \$\endgroup\$ Commented Jun 27, 2018 at 17:20
  • \$\begingroup\$ Another issue is that there isn't a standard mapping of "quality" settings to SSIM thresholds, nor is there any way to determine what quality setting would be needed to avoid significant loss of information. If one loads a JPEG and saves it at a high enough setting, additional quality loss may be avoided but the file will likely get bigger. If one doesn't know what setting was used when a file was produced, it may be hard to determine what setting to use when re-saving it. \$\endgroup\$
    – supercat
    Commented Jun 27, 2018 at 19:16
4
\$\begingroup\$

Nothing like some experimentation. The following bash script (written on Linux, could work on OSX if you have ImageMagick):

  • starting with a first image (named step000.jpg)
  • takes a JPEG file, adds a white dot (to prove this is a new image) and saves it as a (lossless PNG)
  • takes the PNG, and re-compresses it as a JPEG (so we never compress JPEG-to-JPEG, and cannot hypothesize that the software just copies encoded blocks)
  • makes an image that shows the different pixels between the two JPEGs
  • rinse and repeat, using the output JPG of the previous step

The result is that:

  1. there isn't much loss at high JPG qualities
  2. round-off errors eventually settle, after a short number of generations, things don't degrade any more.

Of course all this assumes that the JPEG is saved by the same software with the same parameters each time.

#! /bin/bash
# Runs successive JPEG saves on an image to evaluate JPEG losses

# convert & compare command from imagemagick
# if you use a recent version of IM, set these variables to:
# compare="magick compare"
# convert="magick convert"
convert=convert
compare=compare

dotradius=2
defaultsteps=10
defaultquality=90 # default quality for "convert"

function usage {
        echo "Usage: $0 [quality [steps]]"
        echo ""
        echo "Where:"
        echo "       - 'quality' is the quality factor of the JPEG compression "
        echo "          (1-100, 100 is best, default is $defaultquality)"
        echo "       - 'steps' is the number of successive steps to perform"
        echo "         (default is $defaultsteps)"
        echo ""
        echo "Produces:"
        echo "   - successive saves of a JPEG image to test JPEG-induced losses."
        echo "   - compare images with the original file and the 1st JPEG save."
        echo ""
        echo "Starts from a 'step000.jpg' file in the current directory."
        exit 1
}

[[ -n "$3" ]] && { usage ; exit 1 ; }
steps=${1:-$defaultsteps}
quality=${2:-$defaultquality}    
dotcolor="white" # change this if the top of the image is too clear

echo "Running with $steps steps with quality $quality"

for step in $(seq $steps)
do 
    echo "Step $step of $steps"
    src=$(printf step%03d $(( $step - 1 )) ) 
    dst=$(printf step%03d $(( $step )) )
    dif=$(printf diff%03d $(( $step )) )
    # dot coordinates
    let cxc="2 * $dotradius * $step"
    let cxr="$cxc + $dotradius"
    let cyc="$dotradius * 2"
    let cyr="$dotsradius * 2"

    $convert $src.jpg -fill white -draw "circle $cxc,$cyc,$cxr,$cyr" $dst.png
    $convert $dst.png -quality $quality $dst.jpg
    rm $dst.png
    $compare $src.jpg $dst.jpg $dif.jpg
done

For the time being I won't show the results, I prefer to let you experiment with your own pictures. With enough comments, I'll add a sample.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I was curious about the different software thing. I tried saving 7x from 7 different software. The difference was quite large so I broke it down to see if each application had the same loss. 1 of the apps was responsible for all of the variation. Once I removed the red herring, 6x saves from 6x programs was the same as 6x saves from ImageJ \$\endgroup\$ Commented Jun 27, 2018 at 14:43
  • \$\begingroup\$ There is likely some badly coded software. It is also possible that mixing the algorithms from various apps will prevent the round-off errors from settling, too. \$\endgroup\$
    – xenoid
    Commented Jun 27, 2018 at 14:50
  • \$\begingroup\$ @xiota, it was an odd little program called FLEMinimizer. I don't even remember why I had it in the first place. The others were ImageJ, Matlab, Photoshop, FastStone Image Viewer, Ifranview, and CameraRaw. There was almost no variation at any step between those six. \$\endgroup\$ Commented Jun 27, 2018 at 15:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.