7

I have some experiments with JPEG, the doc said "100 completely disables the JPEG quantization stage."

However, I still got some pixel modification during saving. Here is my code:

import Image
red = [20,30,40,50,60,70];
img = Image.new("RGB", [1, len(red)], (255,255,255))
pix = img.load()

for x in range(0,len(red)):
    pix[0,x] = (red[x],255,255)

img.save('test.jpg',quality=100)

img = Image.open('test.jpg')
pix = img.load()

for x in range(0,len(red)):
    print pix[0,x][0],

I got unexpected output: 22 25 42 45 62 65 What should I do to preserve the pixel value ? Please note that I also tried with PHP using imagejpeg and It gives me the correct value when quality=100.

I can use png to preserve, but I want to know the reason behind this and if there is any option to avoid

Jonathan Root
  • 535
  • 2
  • 14
  • 31
w00d
  • 5,416
  • 12
  • 53
  • 85

3 Answers3

4

JPEG will always carry risk of lossyness, see Is Jpeg lossless when quality is set to 100?.

Your best bet is to use another format, especially if your experiments are for science :) Even if you're forced to start with JPEG (which seems unlikely) you should immediately convert to a lossless format for any kind of analysis and modification.

If you really want to try lossless JPEG work with python you can try jpegtran, "the lossless jpeg image transformation software from the Independent Jpeg Group", but as @Mark notes, this won't get you very far.

By the way, quantization is used in lossy or lossless compression alike, so my guess is that

...100 completely disables the JPEG quantization stage.[1]

simply means that it's not compressed at all.

Community
  • 1
  • 1
askewchan
  • 45,161
  • 17
  • 118
  • 134
4

JPEG consists of many different steps, many of which introduce some loss. By using a sample image containing only red, you've probably run across the worst offender - downsampling or chroma subsampling. Half of the color information is thrown away because the eye is more sensitive to brightness changes than color changes.

Some JPEG encoders can be configured to turn off subsampling, including PIL and Pillow by setting subsampling=0. In any case it won't give you a completely lossless file since there are still other steps that introduce a loss.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 2
    ok there is an option for subsampling in PIL, I set subsampling=0 on img.save() and it gives me the correct 20-70. setting the same value for all red,green,blue also sometimes does the trick. – w00d Mar 18 '13 at 17:14
  • @w00d, can you give me a link to where that's documented? I couldn't find it. – Mark Ransom Mar 18 '13 at 17:18
  • 1
    I guess it's undocumented, I found it in the source: https://bitbucket.org/effbot/pil-2009-raclette/src/cd403356263f039a4a48a1111c7f5cc38686e481/PIL/JpegImagePlugin.py?at=default#cl-437 – w00d Mar 18 '13 at 17:21
  • 1
    @w00d here it is years later, and `Pillow` has documented it: http://pillow.readthedocs.io/en/3.4.x/handbook/image-file-formats.html#jpeg – Mark Ransom Apr 27 '17 at 18:35
  • `subsampling=0` sets it to the highest quality setting, preserving quality but may expand the storage space needed for lower quality images. – Gringo Suave Oct 28 '19 at 06:22
  • @GringoSuave true, but that's not what the question was about. – Mark Ransom Oct 28 '19 at 13:16
3

Believe I've figured out how to keep the current color subsampling and other quality details:

from PIL import Image, JpegImagePlugin as JIP

img = Image.open(filename)
img.save(
    filename + '2.jpg',                 # copy
    format='JPEG',
    exif=img.info['exif'],              # keep EXIF info
    optimize=True,
    qtables=img.quantization,           # keep quality
    subsampling=JIP.get_sampling(img),  # keep color res
)

Per https://www.exiv2.org/tags.html I've found that the YCbCrSubSampling tag is not kept in EXIF in JPEG files:

In JPEG compressed data a JPEG marker is used instead of this tag.

This must be why there is another function in a seemingly out of the way place to to grab it.

(Believe I found it here: https://newbedev.com/determining-jpg-quality-in-python-pil)

Gringo Suave
  • 29,931
  • 6
  • 88
  • 75