Why JPEG compressing an uncompressed image differs its original (FFmpeg, NvJPEG, ...)

Viewed 205

I am currently struggling to understand why recompressing an uncompressed JPEG image differs its original.

It's clear, that JPEG is a lossy compression, but what if the image to compress is already uncompressed, which means all sampling losses are already included? In other words: Downsampling and DCT should be inversable at this point without loosing data.

JPEG algorithm

To make sure losses are not effected by the color space conversion, this step is skipped and YUV images are used.

  1. Compress YUV image to JPEG (image.yuv --> image.yuv.jpg)
  2. Uncompress JPEG image to YUV image (image.yuv.jpg --> image.yuv.jpg.yuv)
  3. Compress YUV image to JPEG (image.yuv.jpg.yuv --> image.yuv.jpg.yuv.jpg)
  4. Uncompress JPEG image to YUV image (image.yuv.jpg.yuv.jpg --> image.yuv.jpg.yuv.jpg.yuv)

Step 1 includes a lossy compression, so we will not deal with this step anymore. For me, intresting is what happens afterwards:

Uncompressing the JPEG image back to YUV (step 2) leads to an image which perfectly fits all sampling steps if compressed again (step 3). So the JPEG image after step 3 should (from my understanding) be exactly the same as after step 1. Also the YUV images after step 4 and step 2 should equal each other.

Looking at the steps for one 8x8 block the following simplified sequence should illustrate what I am trying to descibe. Lets start with the original YUV image, which can only be decompressed loosing all decimal places:

[ 1.123, 2.345, 3.456, ... ]    (YUV)
    DTC + Quantization
[ -26, -3, -6, ... ]            (Quantized frequency space)
    Inverse DTC + Quantization
[ 1, 2, 3, ... ]                (YUV)

Doing this with input, which already matches all steps, which may lead to loss of data afterwards (using round numbers in my example), the decompressed image should match its original:

[ 1, 2, 3, ... ]                (YUV)
    DTC + Quantization
[ -26, -3, -6, ... ]            (Quantized frequency space)
    Inverse DTC + Quantization
[ 1, 2, 3, ... ]                (YUV)

There are also some sources and discussions, which are confirming my idea:

So much for theory. In praxis, I've runned these steps using ffmpeg and Nvidias jpeg samples (using NvJPEGEncoder).

ffmpeg:

#Create YUV image
ffmpeg -y -i image.jpg -s 1920x1080 -pix_fmt yuv420p image.yuv
#YUV to JPEG
ffmpeg -y -s 1920x1080 -pix_fmt yuv420p -i image.yuv image.yuv.jpg
#JPEG TO YUV
ffmpeg -y -i image.yuv.jpg -s 1920x1080 -pix_fmt yuv420p image.yuv.jpg.yuv
#YUV to JPEG
ffmpeg -y -s 1920x1080 -pix_fmt yuv420p -i image.yuv.jpg.yuv image.yuv.jpg.yuv.jpg
#JPEG TO YUV
ffmpeg -y -i image.yuv.jpg.yuv.jpg -s 1920x1080 -pix_fmt yuv420p image.yuv.jpg.yuv.jpg.yuv
#YUV to JPEG
ffmpeg -y -s 1920x1080 -pix_fmt yuv420p -i image.yuv.jpg.yuv.jpg.yuv image.yuv.jpg.yuv.jpg.yuv.jpg

Nvidia:

#Create YUV image
./jpeg_decode num_files 1 image.jpg image.yuv
#YUV to JPEG
./jpeg_encode image.yuv 1920 1080 image.yuv.jpg
#JPEG TO YUV
./jpeg_decode num_files 1 image.yuv.jpg image.yuv.jpg.yuv
#YUV to JPEG
./jpeg_encode image.yuv.jpg.yuv 1920 1080 image.yuv.jpg.yuv.jpg
#JPEG TO YUV
./jpeg_decode num_files 1 image.yuv.jpg.yuv.jpg image.yuv.jpg.yuv.jpg.yuv
#YUV to JPEG
./jpeg_encode image.yuv.jpg.yuv.jpg.yuv 1920 1080 image.yuv.jpg.yuv.jpg.yuv.jpg

But a comparison of the images

  • image.yuv.jpg.yuv and image.yuv.jpg.yuv.jpg.yuv
  • image.yuv.jpg.yuv.jpg and image.yuv.jpg.yuv.jpg.yuv.jpg

showing differences in the files. That brings me to my question why and where the difference gets happen, since from my understanding the files should be equal.

0 Answers
Related