If you make a copy of a copy of a copy, the quality will deteriorate with each subsequent version in a phenomenon called “generation loss.” It’s easy to understand why this happens with actual copier machines. Scanning and printing are based on noisy sensors and physical paper and ink, and the resulting noise will tend to accumulate.
Digital images should not suffer generation loss. In theory, a file can be copied over and over again, and it should be bit-for-bit identical to the original. However, lossy image formats, such as JPEG, can behave like photocopiers. If you simply copy a JPEG file, nothing changes. But if you open a JPEG file in an image editor and then save it, you will get a different JPEG file. Some information can be lost in the process, and compression artifacts will start to accumulate. Do this often enough, and the image will eventually degrade beyond use.
The problems with resaving JPEGs
In this video, you can see what happens to image quality when you re-encode a JPEG image many times.
JPEG offers quality settings that result in a trade-off between compression and visual quality.
But if you just save the JPEG at a high enough quality setting, there won’t be a problem, right? Not exactly. Information that is lost when JPEGs are re-saved cannot be magically recovered. So if you take a JPEG image that was saved with a quality of 70, then re-saving it with a quality of 90 will, of course, not make the image look any better. In fact, it will even be worse. Every additional JPEG encoding will introduce additional loss, even if it is done at a higher quality setting than the original JPEG.
To understand this problem, we have to appreciate how this format uses several mechanisms to reduce the file size of an image, some of which don’t accumulate while others do.
The first is a color space transformation. Digital images are typically represented as pixels containing three separate 8-bit RGB (red, green, blue) values, which are statistically correlated in most images. For example, in a grayscale image, the three channels are completely identical. So if image compression is the goal, RGB is not the best representation. Instead, JPEG uses the YCbCr color space. The Y channel is called luma (the intensity of the light, i.e. the grayscale image), the two other channels, Cb and Cr, are called chroma (the color components). Besides decorrelating the pixel information, this color transformation has another advantage: The human eye is more sensitive to luma than it is to chroma, so in lossy compression, you can get away with more loss in the chroma channels than in the luma channel.
The color space transformation itself already introduces some loss, due to rounding errors and limited precision. If you transform an image containing all 16.7 million different colors from RGB to YCbCr and back, and then count the number of different colors, you’ll end up with only about 4 million different colors; most of the loss is in the red and blue channels.
The YCbCr color transformation by itself does not result in generation loss. It’s a relatively small, one-time loss in color precision, but it does not accumulate. JPEG also does “chroma subsampling”—sometimes called “4:2:0”—that results in only the Y channel being encoded at full resolution; while the Cb and Cr channel resolutions are cut in half both horizontally and vertically. As a result, chroma channels are reduced to one third of the total.
Chroma subsampling contributes to generation loss and can lead to color bleeding or color drifting. The chroma channels become increasingly blurry with each iteration of subsampling/upsampling. For example, this is what happens if you take an image and save it with a JPEG quality of 100 with 4:2:0 chroma subsampling:
While color space transformation and chroma subsampling can lead to generation loss, it isn’t the cause of real loss in JPEGs, though.
The core of JPEG compression is quantization, which is a very simple yet effective mechanism. If you want to compress some sequence of numbers—it doesn’t actually matter whether these numbers represent pixel values, DCT coefficients or something else—the amount of space you need to encode them depends on how large the numbers are. For smaller numbers, less bits are needed.
To make those numbers smaller, you divide them by some number—called a quantization constant—in the encoder, and then multiply it again by that same number in the decoder. The larger this quantization constant, the smaller the encoded values will become. But the image becomes more lossy because we’re rounding everything to integers here (otherwise the numbers wouldn’t really become smaller).
This also explains why re-saving a JPEG file at a higher quality setting than the original is always a bad idea: you’ll get a larger file with more loss than if you would re-save it at the exact same quality setting.
Issues with other image formats
You might expect that JPEG suffers from generation loss because it is a 25-year-old file format, and newer formats are better. But that’s not so. Modern image formats, such as WebP (released in 2010) or BPG (released in 2014) suffer even more from generation loss than JPEG. WebP and BPG use variable-sized, larger macroblocks, which is good for compression, but can result in an error in one part of the image more easily propagating to other parts of the image. This does not mean that WebP and BPG are bad image formats, you just have to be careful in how you use them.
FLIF is a lossless image format that outperforms other lossless image formats. FLIF also has a lossy encoder that modifies the image so that the lossless compression works better on it. It is much less sensitive to generation loss because the format itself is lossless. Generation loss commonly occurs when you significantly modify the image between generations, for example by performing a rotation or resizing.
The color space of FLIF is YCoCg, which does not introduce loss, and there is no chroma subsampling, nor transformation to DCT that introduces rounding errors. Instead of using quantization, FLIF rounds small values to zero and discards a number of bits. This works because the values it encodes are differences (between predicted pixel values and actual pixel values), not absolute values (of DCT coefficients).
Avoiding generation loss
There are only two ways to avoid generation loss:
- Keep the number of generations as close as possible to 1—the generation count has a larger impact on the image quality than the actual quality settings you use. (For example, if you save an image first with a JPEG quality of 85 and then re-save it with a quality of 90, the result will actually be more lossy than if you saved it only once with a quality of 80.)
- Don’t use a lossy format—when editing images, it is best to store the original and intermediate images using lossless image formats like PNG, TIFF, FLIF, or native image editor formats like PSD or XCF. Only when you’re done should the final image be saved using a lossy format like JPEG to reduce the file size. If you later change your mind and want to do some further editing, you can go back to the lossless originals and start from there. When this is not an option—say, you find an image on the internet that you want to edit and reuse, chances are the image is a JPEG file, and the original cannot be found. In this case, one thing you can do is track down the image using Google Image Search, and try to find the earliest generation, i.e. the oldest and highest resolution version of the image.
Cloudinary can help with minimizing the photocopier effect. You can upload the highest resolution, highest quality original image you have available (lossless if possible), especially if you’re using automatic format selection. Cloudinary always keeps your original image as is (adding zero generation loss) and each derived image is encoded directly from the original (adding one generation, which is inevitable). With that approach, you can ensure that your image assets are futureproof. When in the future, higher image qualities and/or resolutions are required or desired, or new image formats become available, it will be an effortless change.
[— This is a sponsored post on behalf of Cloudinary –]