# What happens when you read and save the same JPEG image again over and over?

This question is mostly related to jpeg compression and expected results when you read and save the same jpeg file several times.

Let's say you have read an uncompressed image (originally it has never been compressed). Then read and save it with a jpeg extension in OpenCV. As expected the jpeg file size becomes smaller (uncompressed: 4607 kB, jpeg: 314 kB). Then you read this saved jpeg image in OpenCV again and save it as a jpeg again. When you repeat this loop, what should we expect about the saved image file size, and individual pixel values? In OpenCV, there is this parameter about the JPEQ quality, as shown below.

cv2.imwrite("img.jpg", img, [cv2.IMWRITE_JPEG_QUALITY, 50])

When I repeatedly read the jpeg image and save it as jpeg again, with the same JPEQ quality parameter, it looks like the saved image file sizes are not changing. When I look at the differences between the previously saved image and the current image, there are pixel differences in some of the regions and especially at the bottom side, and right side of the image.

I know a little bit about jpeg compression. I know that it uses DCT on 8x8 blocks, quantize them, and then does an IDCT. Maybe it is because of the quantization, but shouldn't we expect that every saved jpeg image file size should be shrunk even a little bit. Also, what is going on at the bottom and right side of the image (Please see the difference image below)?

import cv2
import matplotlib

img_name = 'image_'
jpg_quality = 50
img = cv2.imread('uncompressed_image.png')

for i in range(10):
if i>0:
path = img_name + str(i)+'.jpg'
img = cv2.imread(path)

new_image_path = img_name + str(i+1)+'.jpg'
cv2.imwrite(new_image_path, img,  [int(cv2.IMWRITE_JPEG_QUALITY), jpg_quality])

if i>0:
f, (ax1, ax2) = plt.subplots(1, 2, sharex=True, sharey=True)
i1 = ax1.imshow(img_orig)
ax1.title.set_text('RGB')

diff = img - cv2.imread(new_image_path)
i2 = ax2.imshow(diff)
ax2.title.set_text('DIFFERENCE')
plt.show()


## 2 Answers

JPEG is lossy compression, and it is allowed to do anything deemed beneficial in representing an image as accurately as possible using the minimum amount of storage while keeping cpu load in check. Using 35 years old tech.

Avoiding additional generational loss would have been possible, but not a part of the design I guess?

You have mentioned the quantizer. That is the main lossy mechanism within JPEG. I dont know your setup, but OpenCV might also do chroma down/upsampling and color transformation between sRGB and BT601 (full swing) that introduce further possibilities for generational loss.

I am not sure why you are getting more errors at the bottom/right edge. JPEG needs to operate on 8x8 pixel blocks. If 4:2:0 downsampling is used (typically for low quality settings), this effectively blows up to image dimensions having to be multiples of 16 pixels. If you image is not, image edges would have to be paddef some way prior to encoding. Depending on how this is done, perhaps it could introduce edge effects that trigger generational losses.

The first application of JPEG to a BMP image is the stage at which data compression (and data loss) happens, amount of which is indicated by the quality parameter and input image statistics (or Entropy).

Any further successive applications of the JPEG algorithm, at the same quality setting and using the same Huffman tables, should not introduce any more compression or data loss, except the numerical roundoff errors occuring at the DCT / IDCT stages. Hence they should produce identical bitstreams, if roundoff errors do not affect the quantization stage output.

What you observe, therefore, are either of those numerical roundoff errors of DCT/IDCT, or the output of a buggy application (which probably isn't).