mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
De-zigzag JPEG's DQT when loading; deprecate convert_dict_qtables
Re-order the JPEG quantization tables to normal order when loading. This wastes a few CPU cycles if you don't need them. But it has the advantage of hiding the zigzag order JPEG implementation detail of these tables completely from Pillow users. This difference has led to cases where: * arrays in zigzag order were taken from a dict and passed directly as a qtables parameter, causing them to be "zigzagged" again by libjpeg. * dicts with lists in normal order being passed to JpegImagePlugin.convert_dict_qtables, causing them to be unnecessarily "de-zigzagged".
This commit is contained in:
parent
7b80e69ed2
commit
9980981c2e
|
@ -446,7 +446,7 @@ class TestFileJpeg:
|
||||||
assert len(im.quantization) == n
|
assert len(im.quantization) == n
|
||||||
reloaded = self.roundtrip(im, qtables="keep")
|
reloaded = self.roundtrip(im, qtables="keep")
|
||||||
assert im.quantization == reloaded.quantization
|
assert im.quantization == reloaded.quantization
|
||||||
assert reloaded.quantization[0].typecode == "B"
|
assert max(reloaded.quantization[0]) <= 255
|
||||||
|
|
||||||
with Image.open("Tests/images/hopper.jpg") as im:
|
with Image.open("Tests/images/hopper.jpg") as im:
|
||||||
qtables = im.quantization
|
qtables = im.quantization
|
||||||
|
@ -458,7 +458,8 @@ class TestFileJpeg:
|
||||||
|
|
||||||
# valid bounds for baseline qtable
|
# valid bounds for baseline qtable
|
||||||
bounds_qtable = [int(s) for s in ("255 1 " * 32).split(None)]
|
bounds_qtable = [int(s) for s in ("255 1 " * 32).split(None)]
|
||||||
self.roundtrip(im, qtables=[bounds_qtable])
|
im2 = self.roundtrip(im, qtables=[bounds_qtable])
|
||||||
|
assert im2.quantization[0] == bounds_qtable
|
||||||
|
|
||||||
# values from wizard.txt in jpeg9-a src package.
|
# values from wizard.txt in jpeg9-a src package.
|
||||||
standard_l_qtable = [
|
standard_l_qtable = [
|
||||||
|
@ -569,6 +570,12 @@ class TestFileJpeg:
|
||||||
assert max(im2.quantization[0]) <= 255
|
assert max(im2.quantization[0]) <= 255
|
||||||
assert max(im2.quantization[1]) <= 255
|
assert max(im2.quantization[1]) <= 255
|
||||||
|
|
||||||
|
def test_convert_dict_qtables_deprecation(self):
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
qtable = {0: [1, 2, 3, 4]}
|
||||||
|
qtable2 = JpegImagePlugin.convert_dict_qtables(qtable)
|
||||||
|
assert qtable == qtable2
|
||||||
|
|
||||||
@pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
|
@pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
|
||||||
def test_load_djpeg(self):
|
def test_load_djpeg(self):
|
||||||
with Image.open(TEST_FILE) as img:
|
with Image.open(TEST_FILE) as img:
|
||||||
|
|
|
@ -252,7 +252,7 @@ def DQT(self, marker):
|
||||||
data = array.array("B" if precision == 1 else "H", s[1:qt_length])
|
data = array.array("B" if precision == 1 else "H", s[1:qt_length])
|
||||||
if sys.byteorder == "little" and precision > 1:
|
if sys.byteorder == "little" and precision > 1:
|
||||||
data.byteswap() # the values are always big-endian
|
data.byteswap() # the values are always big-endian
|
||||||
self.quantization[v & 15] = data
|
self.quantization[v & 15] = [data[i] for i in zigzag_index]
|
||||||
s = s[qt_length:]
|
s = s[qt_length:]
|
||||||
|
|
||||||
|
|
||||||
|
@ -585,9 +585,10 @@ samplings = {
|
||||||
|
|
||||||
|
|
||||||
def convert_dict_qtables(qtables):
|
def convert_dict_qtables(qtables):
|
||||||
qtables = [qtables[key] for key in range(len(qtables)) if key in qtables]
|
warnings.warn(
|
||||||
for idx, table in enumerate(qtables):
|
"convert_dict_qtables is deprecated and will be removed in a future"
|
||||||
qtables[idx] = [table[i] for i in zigzag_index]
|
" release. Conversion is no longer needed.",
|
||||||
|
DeprecationWarning)
|
||||||
return qtables
|
return qtables
|
||||||
|
|
||||||
|
|
||||||
|
@ -668,7 +669,8 @@ def _save(im, fp, filename):
|
||||||
qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)]
|
qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)]
|
||||||
if isinstance(qtables, (tuple, list, dict)):
|
if isinstance(qtables, (tuple, list, dict)):
|
||||||
if isinstance(qtables, dict):
|
if isinstance(qtables, dict):
|
||||||
qtables = convert_dict_qtables(qtables)
|
qtables = [
|
||||||
|
qtables[key] for key in range(len(qtables)) if key in qtables]
|
||||||
elif isinstance(qtables, tuple):
|
elif isinstance(qtables, tuple):
|
||||||
qtables = list(qtables)
|
qtables = list(qtables)
|
||||||
if not (0 < len(qtables) < 5):
|
if not (0 < len(qtables) < 5):
|
||||||
|
|
|
@ -52,19 +52,11 @@ You can get the quantization tables of a JPEG with::
|
||||||
|
|
||||||
im.quantization
|
im.quantization
|
||||||
|
|
||||||
This will return a dict with a number of arrays. You can pass this dict
|
This will return a dict with a number of lists. You can pass this dict
|
||||||
directly as the qtables argument when saving a JPEG.
|
directly as the qtables argument when saving a JPEG.
|
||||||
|
|
||||||
The tables format between im.quantization and quantization in presets differ in
|
The quantization table format in presets is a list with sublists. These formats
|
||||||
3 ways:
|
are interchangeable.
|
||||||
|
|
||||||
1. The base container of the preset is a list with sublists instead of dict.
|
|
||||||
dict[0] -> list[0], dict[1] -> list[1], ...
|
|
||||||
2. Each table in a preset is a list instead of an array.
|
|
||||||
3. The zigzag order is remove in the preset (needed by libjpeg >= 6a).
|
|
||||||
|
|
||||||
You can convert the dict format to the preset format with the
|
|
||||||
:func:`.JpegImagePlugin.convert_dict_qtables()` function.
|
|
||||||
|
|
||||||
Libjpeg ref.:
|
Libjpeg ref.:
|
||||||
https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html
|
https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html
|
||||||
|
|
Loading…
Reference in New Issue
Block a user