Created
March 6, 2013 18:25
-
-
Save etienned/5101740 to your computer and use it in GitHub Desktop.
Patch to add quantization support to JPEG in PIL.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# User etienne <[email protected]> | |
# Date 1318347470 14400 | |
# Node ID 4d7b7e9cb7a8346c712e4e65d5037d90f81b9716 | |
# Parent cd403356263f039a4a48a1111c7f5cc38686e481 | |
Towards adding quantization tables options to the JPEG encoder. | |
diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py | |
--- a/PIL/JpegImagePlugin.py | |
+++ b/PIL/JpegImagePlugin.py | |
@@ -39,6 +39,7 @@ | |
import ImageFile | |
import struct | |
+import array | |
# | |
# Parser | |
@@ -434,6 +435,16 @@ | |
dpi = info.get("dpi", (0, 0)) | |
+ def getsampling(im): | |
+ samplings = { | |
+ (1, 1, 1, 1, 1, 1): 0, | |
+ (2, 1, 1, 1, 1, 1): 1, | |
+ (2, 2, 1, 1, 1, 1): 2, | |
+ } | |
+ layer = im.layer | |
+ sampling = layer[0][1:3] + layer[1][1:3] + layer[2][1:3] | |
+ return samplings.get(sampling, -1) | |
+ | |
subsampling = info.get("subsampling", -1) | |
if subsampling == "4:4:4": | |
subsampling = 0 | |
@@ -441,6 +452,42 @@ | |
subsampling = 1 | |
elif subsampling == "4:1:1": | |
subsampling = 2 | |
+ elif subsampling == "keep": | |
+ subsampling = getsampling(im) | |
+ | |
+ def validate_qtables(qtables): | |
+ if qtables is None: | |
+ return qtables | |
+ if isinstance(qtables, basestring): | |
+ try: | |
+ lines = [int(num) for line in qtables.splitlines() | |
+ for num in line.split('#', 1)[0].split()] | |
+ except ValueError: | |
+ raise Error("Invalid quantization table") | |
+ else: | |
+ qtables = [lines[s:64] for s in xrange((len(lines) + 64) / 64)] | |
+ if isinstance(qtables, (tuple, list, dict)): | |
+ if isinstance(qtables, dict): | |
+ qtables = [qtables[key] for key in xrange(len(qtables)) if qtables.has_key(key)] | |
+ elif isinstance(qtables, tuple): | |
+ qtables = list(qtables) | |
+ if not (0 < len(qtables) < 5): | |
+ raise Error("None or too many quantization tables") | |
+ for idx, table in enumerate(qtables): | |
+ try: | |
+ if len(table) != 64: | |
+ raise | |
+ table = array.array('b', table) | |
+ except TypeError: | |
+ raise Error("Invalid quantization table") | |
+ else: | |
+ qtables[idx] = list(table) | |
+ return qtables | |
+ | |
+ qtables = info.get("qtables") | |
+ if qtables == "keep": | |
+ qtables = info.get("quantization") | |
+ qtables = validate_qtables(qtables) | |
extra = "" | |
@@ -471,6 +518,7 @@ | |
info.get("streamtype", 0), | |
dpi[0], dpi[1], | |
subsampling, | |
+ qtables, | |
extra, | |
) | |
diff --git a/encode.c b/encode.c | |
--- a/encode.c | |
+++ b/encode.c | |
@@ -565,6 +565,57 @@ | |
#include "Jpeg.h" | |
+static unsigned int** get_qtables_arrays(PyObject* qtables) { | |
+ PyObject* tables; | |
+ PyObject* table; | |
+ PyObject* table_data; | |
+ int i, j, num_tables; | |
+ unsigned int **qarrays; | |
+// unsigned int qarray[DCTSIZE2]; | |
+ | |
+ if (qtables == Py_None) { | |
+ return NULL; | |
+ } | |
+ | |
+ if (PySequence_Check(qtables)) { | |
+ tables = PySequence_Fast(qtables, "expected a sequence"); | |
+// if (!tables) | |
+ num_tables = PySequence_Size(qtables); | |
+// if (num_tables < 2 || num_tables > NUM_QUANT_TBLS) | |
+ qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int)); | |
+// if (!qarrays) { | |
+// Py_DECREF(tables); | |
+// PyErr_NoMemory(); | |
+// return NULL; | |
+// } | |
+ for (i = 0; i < num_tables; i++) { | |
+ table = PySequence_Fast_GET_ITEM(tables, i); | |
+// if (!PySequence_Check(table)) { | |
+// return NULL; | |
+// } | |
+// if (PySequence_Size(table) != DCTSIZE2); | |
+ table_data = PySequence_Fast(table, "expected a sequence"); | |
+ // out = (long*) PyMem_Malloc(size * sizeof(long)); | |
+ // if (!out) { | |
+ // Py_DECREF(seq); | |
+ // PyErr_NoMemory(); | |
+ // return NULL; | |
+ // } | |
+ qarrays[i] = (unsigned int*) PyMem_Malloc(DCTSIZE2 * sizeof(unsigned int)); | |
+ for (j = 0; j < DCTSIZE2; j++) { | |
+// if (PyInt_Check(PySequence_Fast_GET_ITEM(table_data, j))) {} | |
+ qarrays[i][j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j)); | |
+ } | |
+ } | |
+ return qarrays; | |
+ | |
+// } else { | |
+// PyErr_SetString(PyExc_ValueError, "Invalid quantization tables"); | |
+ } | |
+ return NULL; | |
+} | |
+ | |
+ | |
PyObject* | |
PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) | |
{ | |
@@ -579,11 +630,13 @@ | |
int streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */ | |
int xdpi = 0, ydpi = 0; | |
int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */ | |
+ PyObject* qtables; | |
+ unsigned int **qarrays = NULL; | |
char* extra = NULL; int extra_size; | |
- if (!PyArg_ParseTuple(args, ARG("ss|iiiiiiiis#", "ss|iiiiiiiiy#"), | |
+ if (!PyArg_ParseTuple(args, ARG("ss|iiiiiiiiOs#", "ss|iiiiiiiiOy#"), | |
&mode, &rawmode, &quality, | |
&progressive, &smooth, &optimize, &streamtype, | |
- &xdpi, &ydpi, &subsampling, &extra, &extra_size)) | |
+ &xdpi, &ydpi, &subsampling, &qtables, &extra, &extra_size)) | |
return NULL; | |
encoder = PyImaging_EncoderNew(sizeof(JPEGENCODERSTATE)); | |
@@ -593,6 +646,8 @@ | |
if (get_packer(encoder, mode, rawmode) < 0) | |
return NULL; | |
+ qarrays = get_qtables_arrays(qtables); | |
+ | |
if (extra && extra_size > 0) { | |
char* p = malloc(extra_size); | |
if (!p) | |
@@ -605,6 +660,7 @@ | |
encoder->encode = ImagingJpegEncode; | |
((JPEGENCODERSTATE*)encoder->state.context)->quality = quality; | |
+ ((JPEGENCODERSTATE*)encoder->state.context)->qtables = **qarrays; | |
((JPEGENCODERSTATE*)encoder->state.context)->subsampling = subsampling; | |
((JPEGENCODERSTATE*)encoder->state.context)->progressive = progressive; | |
((JPEGENCODERSTATE*)encoder->state.context)->smooth = smooth; | |
diff --git a/libImaging/Jpeg.h b/libImaging/Jpeg.h | |
--- a/libImaging/Jpeg.h | |
+++ b/libImaging/Jpeg.h | |
@@ -88,6 +88,9 @@ | |
/* Chroma Subsampling (-1=default, 0=none, 1=medium, 2=high) */ | |
int subsampling; | |
+ /* Custom quantization tables () */ | |
+ unsigned int **qtables; | |
+ | |
/* Extra data (to be injected after header) */ | |
char* extra; int extra_size; | |
diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c | |
--- a/libImaging/JpegEncode.c | |
+++ b/libImaging/JpegEncode.c | |
@@ -146,6 +146,16 @@ | |
if (context->quality > 0) | |
jpeg_set_quality(&context->cinfo, context->quality, 1); | |
+ /* Use custom quantization tables */ | |
+ if (context->qtables) { | |
+ int i; | |
+ for (i = 0; i < NUM_QUANT_TBLS; i++) { | |
+ // TODO: Should add support for none baseline | |
+ jpeg_add_quant_table(&context->cinfo, i, context->qtables[i], | |
+ 1, TRUE); | |
+ } | |
+ } | |
+ | |
/* Set subsampling options */ | |
switch (context->subsampling) | |
{ | |
# HG changeset patch | |
# User etienne <[email protected]> | |
# Date 1318369068 14400 | |
# Node ID 004963994f36fe3cf0025a62d3cdba678a480ede | |
# Parent 4d7b7e9cb7a8346c712e4e65d5037d90f81b9716 | |
Working version of the JPEG encoder quantization tables options. | |
diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py | |
--- a/PIL/JpegImagePlugin.py | |
+++ b/PIL/JpegImagePlugin.py | |
@@ -424,6 +424,15 @@ | |
"YCbCr": "YCbCr", | |
} | |
+zigzag_index = ( 0, 1, 5, 6, 14, 15, 27, 28, | |
+ 2, 4, 7, 13, 16, 26, 29, 42, | |
+ 3, 8, 12, 17, 25, 30, 41, 43, | |
+ 9, 11, 18, 24, 31, 40, 44, 53, | |
+ 10, 19, 23, 32, 39, 45, 52, 54, | |
+ 20, 22, 33, 38, 46, 51, 55, 60, | |
+ 21, 34, 37, 47, 50, 56, 59, 61, | |
+ 35, 36, 48, 49, 57, 58, 62, 63) | |
+ | |
def _save(im, fp, filename): | |
try: | |
@@ -435,6 +444,15 @@ | |
dpi = info.get("dpi", (0, 0)) | |
+ quality = info.get("quality", 0) | |
+ subsampling = info.get("subsampling", -1) | |
+ qtables = info.get("qtables") | |
+ | |
+ if quality == "keep": | |
+ quality = 0 | |
+ subsampling = "keep" | |
+ qtables = "keep" | |
+ | |
def getsampling(im): | |
samplings = { | |
(1, 1, 1, 1, 1, 1): 0, | |
@@ -445,7 +463,6 @@ | |
sampling = layer[0][1:3] + layer[1][1:3] + layer[2][1:3] | |
return samplings.get(sampling, -1) | |
- subsampling = info.get("subsampling", -1) | |
if subsampling == "4:4:4": | |
subsampling = 0 | |
elif subsampling == "4:2:2": | |
@@ -453,6 +470,8 @@ | |
elif subsampling == "4:1:1": | |
subsampling = 2 | |
elif subsampling == "keep": | |
+ if im.format != "JPEG": | |
+ raise SyntaxError("Cannot use 'keep' when original image is not a JPEG") | |
subsampling = getsampling(im) | |
def validate_qtables(qtables): | |
@@ -463,30 +482,33 @@ | |
lines = [int(num) for line in qtables.splitlines() | |
for num in line.split('#', 1)[0].split()] | |
except ValueError: | |
- raise Error("Invalid quantization table") | |
+ raise SyntaxError("Invalid quantization table") | |
else: | |
- qtables = [lines[s:64] for s in xrange((len(lines) + 64) / 64)] | |
+ qtables = [lines[s:s+64] for s in xrange(0, len(lines), 64)] | |
if isinstance(qtables, (tuple, list, dict)): | |
if isinstance(qtables, dict): | |
qtables = [qtables[key] for key in xrange(len(qtables)) if qtables.has_key(key)] | |
+ for idx, table in enumerate(qtables): | |
+ qtables[idx] = [table[i] for i in zigzag_index] | |
elif isinstance(qtables, tuple): | |
qtables = list(qtables) | |
if not (0 < len(qtables) < 5): | |
- raise Error("None or too many quantization tables") | |
+ raise SyntaxError("None or too many quantization tables") | |
for idx, table in enumerate(qtables): | |
try: | |
if len(table) != 64: | |
raise | |
table = array.array('b', table) | |
except TypeError: | |
- raise Error("Invalid quantization table") | |
+ raise SyntaxError("Invalid quantization table") | |
else: | |
qtables[idx] = list(table) | |
return qtables | |
- qtables = info.get("qtables") | |
if qtables == "keep": | |
- qtables = info.get("quantization") | |
+ if im.format != "JPEG": | |
+ raise SyntaxError("Cannot use 'keep' when original image is not a JPEG") | |
+ qtables = getattr(im, "quantization", None) | |
qtables = validate_qtables(qtables) | |
extra = "" | |
@@ -508,7 +530,7 @@ | |
# get keyword arguments | |
im.encoderconfig = ( | |
- info.get("quality", 0), | |
+ quality, | |
# "progressive" is the official name, but older documentation | |
# says "progression" | |
# FIXME: issue a warning if the wrong form is used (post-1.1.7) | |
diff --git a/encode.c b/encode.c | |
--- a/encode.c | |
+++ b/encode.c | |
@@ -571,48 +571,64 @@ | |
PyObject* table_data; | |
int i, j, num_tables; | |
unsigned int **qarrays; | |
-// unsigned int qarray[DCTSIZE2]; | |
if (qtables == Py_None) { | |
return NULL; | |
} | |
- if (PySequence_Check(qtables)) { | |
- tables = PySequence_Fast(qtables, "expected a sequence"); | |
+ if (!PySequence_Check(qtables)) { | |
+ PyErr_SetString(PyExc_ValueError, "Invalid quantization tables"); | |
+ return NULL; | |
+ } | |
+ | |
+ tables = PySequence_Fast(qtables, "expected a sequence"); | |
// if (!tables) | |
- num_tables = PySequence_Size(qtables); | |
-// if (num_tables < 2 || num_tables > NUM_QUANT_TBLS) | |
- qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int)); | |
-// if (!qarrays) { | |
-// Py_DECREF(tables); | |
-// PyErr_NoMemory(); | |
-// return NULL; | |
-// } | |
- for (i = 0; i < num_tables; i++) { | |
- table = PySequence_Fast_GET_ITEM(tables, i); | |
-// if (!PySequence_Check(table)) { | |
-// return NULL; | |
-// } | |
-// if (PySequence_Size(table) != DCTSIZE2); | |
- table_data = PySequence_Fast(table, "expected a sequence"); | |
- // out = (long*) PyMem_Malloc(size * sizeof(long)); | |
- // if (!out) { | |
- // Py_DECREF(seq); | |
- // PyErr_NoMemory(); | |
- // return NULL; | |
- // } | |
- qarrays[i] = (unsigned int*) PyMem_Malloc(DCTSIZE2 * sizeof(unsigned int)); | |
- for (j = 0; j < DCTSIZE2; j++) { | |
+ num_tables = PySequence_Size(qtables); | |
+ if (num_tables < 2 || num_tables > NUM_QUANT_TBLS) { | |
+ PyErr_SetString(PyExc_ValueError, "Not a valid numbers of quantization tables. Should be between 2 and 4."); | |
+ return NULL; | |
+ } | |
+ qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int)); | |
+ if (!qarrays) { | |
+ Py_DECREF(tables); | |
+ PyErr_NoMemory(); | |
+ PyErr_SetString(PyExc_ValueError, "Not enough memory"); | |
+ return NULL; | |
+ } | |
+ for (i = 0; i < num_tables; i++) { | |
+ table = PySequence_Fast_GET_ITEM(tables, i); | |
+ if (!PySequence_Check(table)) { | |
+ Py_DECREF(tables); | |
+ PyErr_SetString(PyExc_ValueError, "Invalid quantization tables"); | |
+ return NULL; | |
+ } | |
+ if (PySequence_Size(table) != DCTSIZE2) { | |
+ Py_DECREF(tables); | |
+ PyErr_SetString(PyExc_ValueError, "Invalid quantization tables"); | |
+ return NULL; | |
+ } | |
+ table_data = PySequence_Fast(table, "expected a sequence"); | |
+ qarrays[i] = (unsigned int*) PyMem_Malloc(DCTSIZE2 * sizeof(unsigned int)); | |
+ if (!qarrays[i]) { | |
+ Py_DECREF(tables); | |
+ PyErr_NoMemory(); | |
+ PyErr_SetString(PyExc_ValueError, "Not enough memory"); | |
+ return NULL; | |
+ } | |
+ for (j = 0; j < DCTSIZE2; j++) { | |
// if (PyInt_Check(PySequence_Fast_GET_ITEM(table_data, j))) {} | |
- qarrays[i][j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j)); | |
- } | |
+ qarrays[i][j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j)); | |
} | |
- return qarrays; | |
+ } | |
-// } else { | |
-// PyErr_SetString(PyExc_ValueError, "Invalid quantization tables"); | |
+ Py_DECREF(tables); | |
+ | |
+ if (PyErr_Occurred()) { | |
+ PyMem_Free(qarrays); | |
+ qarrays = NULL; | |
} | |
- return NULL; | |
+ | |
+ return qarrays; | |
} | |
@@ -660,7 +676,7 @@ | |
encoder->encode = ImagingJpegEncode; | |
((JPEGENCODERSTATE*)encoder->state.context)->quality = quality; | |
- ((JPEGENCODERSTATE*)encoder->state.context)->qtables = **qarrays; | |
+ ((JPEGENCODERSTATE*)encoder->state.context)->qtables = qarrays; | |
((JPEGENCODERSTATE*)encoder->state.context)->subsampling = subsampling; | |
((JPEGENCODERSTATE*)encoder->state.context)->progressive = progressive; | |
((JPEGENCODERSTATE*)encoder->state.context)->smooth = smooth; | |
diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c | |
--- a/libImaging/JpegEncode.c | |
+++ b/libImaging/JpegEncode.c | |
@@ -143,17 +143,21 @@ | |
/* Compressor configuration */ | |
jpeg_set_defaults(&context->cinfo); | |
- if (context->quality > 0) | |
- jpeg_set_quality(&context->cinfo, context->quality, 1); | |
- | |
+ | |
/* Use custom quantization tables */ | |
if (context->qtables) { | |
int i; | |
- for (i = 0; i < NUM_QUANT_TBLS; i++) { | |
+ int quality = 100; | |
+ if (context->quality > 0) { | |
+ quality = context->quality; | |
+ } | |
+ for (i = 0; i < sizeof(context->qtables)/sizeof(unsigned int); i++) { | |
// TODO: Should add support for none baseline | |
jpeg_add_quant_table(&context->cinfo, i, context->qtables[i], | |
- 1, TRUE); | |
+ quality, TRUE); | |
} | |
+ } else if (context->quality > 0) { | |
+ jpeg_set_quality(&context->cinfo, context->quality, 1); | |
} | |
/* Set subsampling options */ | |
# HG changeset patch | |
# User etienne <[email protected]> | |
# Date 1319338692 14400 | |
# Node ID 2615354d48af78f4181ef2d5de69f6edb5121395 | |
# Parent 004963994f36fe3cf0025a62d3cdba678a480ede | |
Added JPEG quality presets. | |
diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py | |
--- a/PIL/JpegImagePlugin.py | |
+++ b/PIL/JpegImagePlugin.py | |
@@ -37,6 +37,10 @@ | |
import Image | |
import ImageFile | |
+try: | |
+ from JpegPresets import presets | |
+except ImportError: | |
+ presets = {} | |
import struct | |
import array | |
@@ -452,6 +456,18 @@ | |
quality = 0 | |
subsampling = "keep" | |
qtables = "keep" | |
+ elif quality in presets: | |
+ preset = presets[quality] | |
+ quality = 0 | |
+ subsampling = preset.get('subsampling', -1) | |
+ qtables = preset.get('quantization') | |
+ elif not isinstance(quality, int): | |
+ raise SyntaxError("Invalid quality setting") | |
+ else: | |
+ if subsampling in presets: | |
+ subsampling = presets[subsampling].get('subsampling', -1) | |
+ if qtables in presets: | |
+ qtables = presets[qtables].get('quantization') | |
def getsampling(im): | |
samplings = { | |
@@ -482,7 +498,7 @@ | |
lines = [int(num) for line in qtables.splitlines() | |
for num in line.split('#', 1)[0].split()] | |
except ValueError: | |
- raise SyntaxError("Invalid quantization table") | |
+ raise ValueError("Invalid quantization table") | |
else: | |
qtables = [lines[s:s+64] for s in xrange(0, len(lines), 64)] | |
if isinstance(qtables, (tuple, list, dict)): | |
@@ -493,14 +509,14 @@ | |
elif isinstance(qtables, tuple): | |
qtables = list(qtables) | |
if not (0 < len(qtables) < 5): | |
- raise SyntaxError("None or too many quantization tables") | |
+ raise ValueError("None or too many quantization tables") | |
for idx, table in enumerate(qtables): | |
try: | |
if len(table) != 64: | |
raise | |
table = array.array('b', table) | |
except TypeError: | |
- raise SyntaxError("Invalid quantization table") | |
+ raise ValueError("Invalid quantization table") | |
else: | |
qtables[idx] = list(table) | |
return qtables | |
diff --git a/PIL/JpegPresets.py b/PIL/JpegPresets.py | |
new file mode 100644 | |
--- /dev/null | |
+++ b/PIL/JpegPresets.py | |
@@ -0,0 +1,205 @@ | |
+""" | |
+JPEG quality settings equivalent to the Photoshop settings. | |
+ | |
+More presets can be added to the presets dict if needed. | |
+ | |
+Can be use when saving JPEG file. | |
+ | |
+To apply the preset, specify: | |
+ | |
+ - quality=preset name | |
+ | |
+To apply only the quantization table: | |
+ | |
+- qtables=preset name | |
+ | |
+To apply only the subsampling setting: | |
+ | |
+- subsampling=preset name | |
+ | |
+Example: | |
+ | |
+ im.save("image_name.jpg", quality="web_high") | |
+ | |
+""" | |
+ | |
+presets = { 'web_maximum': {'subsampling': 0, # "4:4:4" | |
+ 'quantization': [ | |
+ [ 1, 1, 1, 1, 1, 1, 1, 1, | |
+ 1, 1, 1, 1, 1, 1, 1, 1, | |
+ 1, 1, 1, 1, 1, 1, 1, 2, | |
+ 1, 1, 1, 1, 1, 1, 2, 2, | |
+ 1, 1, 1, 1, 1, 2, 2, 3, | |
+ 1, 1, 1, 1, 2, 2, 3, 3, | |
+ 1, 1, 1, 2, 2, 3, 3, 3, | |
+ 1, 1, 2, 2, 3, 3, 3, 3], | |
+ [ 1, 1, 1, 2, 2, 3, 3, 3, | |
+ 1, 1, 1, 2, 3, 3, 3, 3, | |
+ 1, 1, 1, 3, 3, 3, 3, 3, | |
+ 2, 2, 3, 3, 3, 3, 3, 3, | |
+ 2, 3, 3, 3, 3, 3, 3, 3, | |
+ 3, 3, 3, 3, 3, 3, 3, 3, | |
+ 3, 3, 3, 3, 3, 3, 3, 3, | |
+ 3, 3, 3, 3, 3, 3, 3, 3] | |
+ ]}, | |
+ | |
+ 'medium': {'subsampling': 2, # "4:1:1" | |
+ 'quantization': [ | |
+ [12, 8, 8, 12, 17, 21, 24, 17, | |
+ 8, 9, 9, 11, 15, 19, 12, 12, | |
+ 8, 9, 10, 12, 19, 12, 12, 12, | |
+ 12, 11, 12, 21, 12, 12, 12, 12, | |
+ 17, 15, 19, 12, 12, 12, 12, 12, | |
+ 21, 19, 12, 12, 12, 12, 12, 12, | |
+ 24, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12], | |
+ [13, 11, 13, 16, 20, 20, 17, 17, | |
+ 11, 14, 14, 14, 14, 12, 12, 12, | |
+ 13, 14, 14, 14, 12, 12, 12, 12, | |
+ 16, 14, 14, 12, 12, 12, 12, 12, | |
+ 20, 14, 12, 12, 12, 12, 12, 12, | |
+ 20, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12] | |
+ ]}, | |
+ | |
+ 'web_medium': {'subsampling': 2, # "4:1:1" | |
+ 'quantization': [ | |
+ [16, 11, 11, 16, 23, 27, 31, 30, | |
+ 11, 12, 12, 15, 20, 23, 23, 30, | |
+ 11, 12, 13, 16, 23, 26, 35, 47, | |
+ 16, 15, 16, 23, 26, 37, 47, 64, | |
+ 23, 20, 23, 26, 39, 51, 64, 64, | |
+ 27, 23, 26, 37, 51, 64, 64, 64, | |
+ 31, 23, 35, 47, 64, 64, 64, 64, | |
+ 30, 30, 47, 64, 64, 64, 64, 64], | |
+ [17, 15, 17, 21, 20, 26, 38, 48, | |
+ 15, 19, 18, 17, 20, 26, 35, 43, | |
+ 17, 18, 20, 22, 26, 30, 46, 53, | |
+ 21, 17, 22, 28, 30, 39, 53, 64, | |
+ 20, 20, 26, 30, 39, 48, 64, 64, | |
+ 26, 26, 30, 39, 48, 63, 64, 64, | |
+ 38, 35, 46, 53, 64, 64, 64, 64, | |
+ 48, 43, 53, 64, 64, 64, 64, 64] | |
+ ]}, | |
+ | |
+ 'maximum': {'subsampling': 0, # "4:4:4" | |
+ 'quantization': [ | |
+ [ 2, 2, 2, 2, 3, 4, 5, 6, | |
+ 2, 2, 2, 2, 3, 4, 5, 6, | |
+ 2, 2, 2, 2, 4, 5, 7, 9, | |
+ 2, 2, 2, 4, 5, 7, 9, 12, | |
+ 3, 3, 4, 5, 8, 10, 12, 12, | |
+ 4, 4, 5, 7, 10, 12, 12, 12, | |
+ 5, 5, 7, 9, 12, 12, 12, 12, | |
+ 6, 6, 9, 12, 12, 12, 12, 12], | |
+ [ 3, 3, 5, 9, 13, 15, 15, 15, | |
+ 3, 4, 6, 10, 14, 12, 12, 12, | |
+ 5, 6, 9, 14, 12, 12, 12, 12, | |
+ 9, 10, 14, 12, 12, 12, 12, 12, | |
+ 13, 14, 12, 12, 12, 12, 12, 12, | |
+ 15, 12, 12, 12, 12, 12, 12, 12, | |
+ 15, 12, 12, 12, 12, 12, 12, 12, | |
+ 15, 12, 12, 12, 12, 12, 12, 12] | |
+ ]}, | |
+ | |
+ 'high': {'subsampling': 0, # "4:4:4" | |
+ 'quantization': [ | |
+ [ 6, 4, 4, 6, 9, 11, 12, 16, | |
+ 4, 5, 5, 6, 8, 10, 12, 12, | |
+ 4, 5, 5, 6, 10, 12, 12, 12, | |
+ 6, 6, 6, 11, 12, 12, 12, 12, | |
+ 9, 8, 10, 12, 12, 12, 12, 12, | |
+ 11, 10, 12, 12, 12, 12, 12, 12, | |
+ 12, 12, 12, 12, 12, 12, 12, 12, | |
+ 16, 12, 12, 12, 12, 12, 12, 12], | |
+ [ 7, 7, 13, 24, 20, 20, 17, 17, | |
+ 7, 12, 16, 14, 14, 12, 12, 12, | |
+ 13, 16, 14, 14, 12, 12, 12, 12, | |
+ 24, 14, 14, 12, 12, 12, 12, 12, | |
+ 20, 14, 12, 12, 12, 12, 12, 12, | |
+ 20, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12] | |
+ ]}, | |
+ | |
+ 'web_high': {'subsampling': 0, # "4:4:4" | |
+ 'quantization': [ | |
+ [ 6, 4, 4, 6, 9, 11, 12, 16, | |
+ 4, 5, 5, 6, 8, 10, 12, 12, | |
+ 4, 5, 5, 6, 10, 12, 14, 19, | |
+ 6, 6, 6, 11, 12, 15, 19, 28, | |
+ 9, 8, 10, 12, 16, 20, 27, 31, | |
+ 11, 10, 12, 15, 20, 27, 31, 31, | |
+ 12, 12, 14, 19, 27, 31, 31, 31, | |
+ 16, 12, 19, 28, 31, 31, 31, 31], | |
+ [ 7, 7, 13, 24, 26, 31, 31, 31, | |
+ 7, 12, 16, 21, 31, 31, 31, 31, | |
+ 13, 16, 17, 31, 31, 31, 31, 31, | |
+ 24, 21, 31, 31, 31, 31, 31, 31, | |
+ 26, 31, 31, 31, 31, 31, 31, 31, | |
+ 31, 31, 31, 31, 31, 31, 31, 31, | |
+ 31, 31, 31, 31, 31, 31, 31, 31, | |
+ 31, 31, 31, 31, 31, 31, 31, 31] | |
+ ]}, | |
+ | |
+ 'web_very_high': {'subsampling': 0, # "4:4:4" | |
+ 'quantization': [ | |
+ [ 2, 2, 2, 2, 3, 4, 5, 6, | |
+ 2, 2, 2, 2, 3, 4, 5, 6, | |
+ 2, 2, 2, 2, 4, 5, 7, 9, | |
+ 2, 2, 2, 4, 5, 7, 9, 12, | |
+ 3, 3, 4, 5, 8, 10, 12, 12, | |
+ 4, 4, 5, 7, 10, 12, 12, 12, | |
+ 5, 5, 7, 9, 12, 12, 12, 12, | |
+ 6, 6, 9, 12, 12, 12, 12, 12], | |
+ [ 3, 3, 5, 9, 13, 15, 15, 15, | |
+ 3, 4, 6, 11, 14, 12, 12, 12, | |
+ 5, 6, 9, 14, 12, 12, 12, 12, | |
+ 9, 11, 14, 12, 12, 12, 12, 12, | |
+ 13, 14, 12, 12, 12, 12, 12, 12, | |
+ 15, 12, 12, 12, 12, 12, 12, 12, | |
+ 15, 12, 12, 12, 12, 12, 12, 12, | |
+ 15, 12, 12, 12, 12, 12, 12, 12] | |
+ ]}, | |
+ | |
+ 'web_low': {'subsampling': 2, # "4:1:1" | |
+ 'quantization': [ | |
+ [20, 16, 25, 39, 50, 46, 62, 68, | |
+ 16, 18, 23, 38, 38, 53, 65, 68, | |
+ 25, 23, 31, 38, 53, 65, 68, 68, | |
+ 39, 38, 38, 53, 65, 68, 68, 68, | |
+ 50, 38, 53, 65, 68, 68, 68, 68, | |
+ 46, 53, 65, 68, 68, 68, 68, 68, | |
+ 62, 65, 68, 68, 68, 68, 68, 68, | |
+ 68, 68, 68, 68, 68, 68, 68, 68], | |
+ [21, 25, 32, 38, 54, 68, 68, 68, | |
+ 25, 28, 24, 38, 54, 68, 68, 68, | |
+ 32, 24, 32, 43, 66, 68, 68, 68, | |
+ 38, 38, 43, 53, 68, 68, 68, 68, | |
+ 54, 54, 66, 68, 68, 68, 68, 68, | |
+ 68, 68, 68, 68, 68, 68, 68, 68, | |
+ 68, 68, 68, 68, 68, 68, 68, 68, | |
+ 68, 68, 68, 68, 68, 68, 68, 68] | |
+ ]}, | |
+ | |
+ 'low': {'subsampling': 2, # "4:1:1" | |
+ 'quantization': [ | |
+ [18, 14, 14, 21, 30, 35, 34, 17, | |
+ 14, 16, 16, 19, 26, 23, 12, 12, | |
+ 14, 16, 17, 21, 23, 12, 12, 12, | |
+ 21, 19, 21, 23, 12, 12, 12, 12, | |
+ 30, 26, 23, 12, 12, 12, 12, 12, | |
+ 35, 23, 12, 12, 12, 12, 12, 12, | |
+ 34, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12], | |
+ [20, 19, 22, 27, 20, 20, 17, 17, | |
+ 19, 25, 23, 14, 14, 12, 12, 12, | |
+ 22, 23, 14, 14, 12, 12, 12, 12, | |
+ 27, 14, 14, 12, 12, 12, 12, 12, | |
+ 20, 14, 12, 12, 12, 12, 12, 12, | |
+ 20, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12] | |
+ ]} | |
+ } | |
\ No newline at end of file | |
# HG changeset patch | |
# User etienne <[email protected]> | |
# Date 1327673657 18000 | |
# Node ID 8ffbda99eee5f9264ea102ee92bd084b0c1f03c3 | |
# Parent 2615354d48af78f4181ef2d5de69f6edb5121395 | |
Fixed PyErr_NoMemory usage in get_qtables_arrays function. | |
diff --git a/encode.c b/encode.c | |
--- a/encode.c | |
+++ b/encode.c | |
@@ -582,7 +582,6 @@ | |
} | |
tables = PySequence_Fast(qtables, "expected a sequence"); | |
-// if (!tables) | |
num_tables = PySequence_Size(qtables); | |
if (num_tables < 2 || num_tables > NUM_QUANT_TBLS) { | |
PyErr_SetString(PyExc_ValueError, "Not a valid numbers of quantization tables. Should be between 2 and 4."); | |
@@ -591,9 +590,7 @@ | |
qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int)); | |
if (!qarrays) { | |
Py_DECREF(tables); | |
- PyErr_NoMemory(); | |
- PyErr_SetString(PyExc_ValueError, "Not enough memory"); | |
- return NULL; | |
+ return PyErr_NoMemory(); | |
} | |
for (i = 0; i < num_tables; i++) { | |
table = PySequence_Fast_GET_ITEM(tables, i); | |
@@ -611,12 +608,9 @@ | |
qarrays[i] = (unsigned int*) PyMem_Malloc(DCTSIZE2 * sizeof(unsigned int)); | |
if (!qarrays[i]) { | |
Py_DECREF(tables); | |
- PyErr_NoMemory(); | |
- PyErr_SetString(PyExc_ValueError, "Not enough memory"); | |
- return NULL; | |
+ return PyErr_NoMemory(); | |
} | |
for (j = 0; j < DCTSIZE2; j++) { | |
-// if (PyInt_Check(PySequence_Fast_GET_ITEM(table_data, j))) {} | |
qarrays[i][j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j)); | |
} | |
} | |
# HG changeset patch | |
# User etienne <[email protected]> | |
# Date 1327673837 18000 | |
# Node ID d62f6ca0b5ea7c8a715ad178e8cf6dbc29026b2d | |
# Parent 8ffbda99eee5f9264ea102ee92bd084b0c1f03c3 | |
Removed try…except when importing JPEG presets. | |
diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py | |
--- a/PIL/JpegImagePlugin.py | |
+++ b/PIL/JpegImagePlugin.py | |
@@ -37,10 +37,7 @@ | |
import Image | |
import ImageFile | |
-try: | |
- from JpegPresets import presets | |
-except ImportError: | |
- presets = {} | |
+from JpegPresets import presets | |
import struct | |
import array | |
# HG changeset patch | |
# User etienne <[email protected]> | |
# Date 1327674340 18000 | |
# Node ID 1ea58a69376c30d96c85834388810c69f978d0d0 | |
# Parent d62f6ca0b5ea7c8a715ad178e8cf6dbc29026b2d | |
Changed SyntaxError(s) to ValueError in JPEG _save function. | |
diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py | |
--- a/PIL/JpegImagePlugin.py | |
+++ b/PIL/JpegImagePlugin.py | |
@@ -459,7 +459,7 @@ | |
subsampling = preset.get('subsampling', -1) | |
qtables = preset.get('quantization') | |
elif not isinstance(quality, int): | |
- raise SyntaxError("Invalid quality setting") | |
+ raise ValueError("Invalid quality setting") | |
else: | |
if subsampling in presets: | |
subsampling = presets[subsampling].get('subsampling', -1) | |
@@ -484,7 +484,7 @@ | |
subsampling = 2 | |
elif subsampling == "keep": | |
if im.format != "JPEG": | |
- raise SyntaxError("Cannot use 'keep' when original image is not a JPEG") | |
+ raise ValueError("Cannot use 'keep' when original image is not a JPEG") | |
subsampling = getsampling(im) | |
def validate_qtables(qtables): | |
@@ -520,7 +520,7 @@ | |
if qtables == "keep": | |
if im.format != "JPEG": | |
- raise SyntaxError("Cannot use 'keep' when original image is not a JPEG") | |
+ raise ValueError("Cannot use 'keep' when original image is not a JPEG") | |
qtables = getattr(im, "quantization", None) | |
qtables = validate_qtables(qtables) | |
# HG changeset patch | |
# User etienne <[email protected]> | |
# Date 1327674611 18000 | |
# Node ID 5462f071739bb29df0d7ceede421ba19a7d14eb1 | |
# Parent 1ea58a69376c30d96c85834388810c69f978d0d0 | |
Reordered JPEG presets in a logical organisation. | |
diff --git a/PIL/JpegPresets.py b/PIL/JpegPresets.py | |
--- a/PIL/JpegPresets.py | |
+++ b/PIL/JpegPresets.py | |
@@ -23,44 +23,25 @@ | |
""" | |
-presets = { 'web_maximum': {'subsampling': 0, # "4:4:4" | |
+presets = { | |
+ 'web_low': {'subsampling': 2, # "4:1:1" | |
'quantization': [ | |
- [ 1, 1, 1, 1, 1, 1, 1, 1, | |
- 1, 1, 1, 1, 1, 1, 1, 1, | |
- 1, 1, 1, 1, 1, 1, 1, 2, | |
- 1, 1, 1, 1, 1, 1, 2, 2, | |
- 1, 1, 1, 1, 1, 2, 2, 3, | |
- 1, 1, 1, 1, 2, 2, 3, 3, | |
- 1, 1, 1, 2, 2, 3, 3, 3, | |
- 1, 1, 2, 2, 3, 3, 3, 3], | |
- [ 1, 1, 1, 2, 2, 3, 3, 3, | |
- 1, 1, 1, 2, 3, 3, 3, 3, | |
- 1, 1, 1, 3, 3, 3, 3, 3, | |
- 2, 2, 3, 3, 3, 3, 3, 3, | |
- 2, 3, 3, 3, 3, 3, 3, 3, | |
- 3, 3, 3, 3, 3, 3, 3, 3, | |
- 3, 3, 3, 3, 3, 3, 3, 3, | |
- 3, 3, 3, 3, 3, 3, 3, 3] | |
- ]}, | |
- | |
- 'medium': {'subsampling': 2, # "4:1:1" | |
- 'quantization': [ | |
- [12, 8, 8, 12, 17, 21, 24, 17, | |
- 8, 9, 9, 11, 15, 19, 12, 12, | |
- 8, 9, 10, 12, 19, 12, 12, 12, | |
- 12, 11, 12, 21, 12, 12, 12, 12, | |
- 17, 15, 19, 12, 12, 12, 12, 12, | |
- 21, 19, 12, 12, 12, 12, 12, 12, | |
- 24, 12, 12, 12, 12, 12, 12, 12, | |
- 17, 12, 12, 12, 12, 12, 12, 12], | |
- [13, 11, 13, 16, 20, 20, 17, 17, | |
- 11, 14, 14, 14, 14, 12, 12, 12, | |
- 13, 14, 14, 14, 12, 12, 12, 12, | |
- 16, 14, 14, 12, 12, 12, 12, 12, | |
- 20, 14, 12, 12, 12, 12, 12, 12, | |
- 20, 12, 12, 12, 12, 12, 12, 12, | |
- 17, 12, 12, 12, 12, 12, 12, 12, | |
- 17, 12, 12, 12, 12, 12, 12, 12] | |
+ [20, 16, 25, 39, 50, 46, 62, 68, | |
+ 16, 18, 23, 38, 38, 53, 65, 68, | |
+ 25, 23, 31, 38, 53, 65, 68, 68, | |
+ 39, 38, 38, 53, 65, 68, 68, 68, | |
+ 50, 38, 53, 65, 68, 68, 68, 68, | |
+ 46, 53, 65, 68, 68, 68, 68, 68, | |
+ 62, 65, 68, 68, 68, 68, 68, 68, | |
+ 68, 68, 68, 68, 68, 68, 68, 68], | |
+ [21, 25, 32, 38, 54, 68, 68, 68, | |
+ 25, 28, 24, 38, 54, 68, 68, 68, | |
+ 32, 24, 32, 43, 66, 68, 68, 68, | |
+ 38, 38, 43, 53, 68, 68, 68, 68, | |
+ 54, 54, 66, 68, 68, 68, 68, 68, | |
+ 68, 68, 68, 68, 68, 68, 68, 68, | |
+ 68, 68, 68, 68, 68, 68, 68, 68, | |
+ 68, 68, 68, 68, 68, 68, 68, 68] | |
]}, | |
'web_medium': {'subsampling': 2, # "4:1:1" | |
@@ -83,7 +64,27 @@ | |
48, 43, 53, 64, 64, 64, 64, 64] | |
]}, | |
- 'maximum': {'subsampling': 0, # "4:4:4" | |
+ 'web_high': {'subsampling': 0, # "4:4:4" | |
+ 'quantization': [ | |
+ [ 6, 4, 4, 6, 9, 11, 12, 16, | |
+ 4, 5, 5, 6, 8, 10, 12, 12, | |
+ 4, 5, 5, 6, 10, 12, 14, 19, | |
+ 6, 6, 6, 11, 12, 15, 19, 28, | |
+ 9, 8, 10, 12, 16, 20, 27, 31, | |
+ 11, 10, 12, 15, 20, 27, 31, 31, | |
+ 12, 12, 14, 19, 27, 31, 31, 31, | |
+ 16, 12, 19, 28, 31, 31, 31, 31], | |
+ [ 7, 7, 13, 24, 26, 31, 31, 31, | |
+ 7, 12, 16, 21, 31, 31, 31, 31, | |
+ 13, 16, 17, 31, 31, 31, 31, 31, | |
+ 24, 21, 31, 31, 31, 31, 31, 31, | |
+ 26, 31, 31, 31, 31, 31, 31, 31, | |
+ 31, 31, 31, 31, 31, 31, 31, 31, | |
+ 31, 31, 31, 31, 31, 31, 31, 31, | |
+ 31, 31, 31, 31, 31, 31, 31, 31] | |
+ ]}, | |
+ | |
+ 'web_very_high': {'subsampling': 0, # "4:4:4" | |
'quantization': [ | |
[ 2, 2, 2, 2, 3, 4, 5, 6, | |
2, 2, 2, 2, 3, 4, 5, 6, | |
@@ -94,15 +95,74 @@ | |
5, 5, 7, 9, 12, 12, 12, 12, | |
6, 6, 9, 12, 12, 12, 12, 12], | |
[ 3, 3, 5, 9, 13, 15, 15, 15, | |
- 3, 4, 6, 10, 14, 12, 12, 12, | |
+ 3, 4, 6, 11, 14, 12, 12, 12, | |
5, 6, 9, 14, 12, 12, 12, 12, | |
- 9, 10, 14, 12, 12, 12, 12, 12, | |
+ 9, 11, 14, 12, 12, 12, 12, 12, | |
13, 14, 12, 12, 12, 12, 12, 12, | |
15, 12, 12, 12, 12, 12, 12, 12, | |
15, 12, 12, 12, 12, 12, 12, 12, | |
15, 12, 12, 12, 12, 12, 12, 12] | |
]}, | |
+ 'web_maximum': {'subsampling': 0, # "4:4:4" | |
+ 'quantization': [ | |
+ [ 1, 1, 1, 1, 1, 1, 1, 1, | |
+ 1, 1, 1, 1, 1, 1, 1, 1, | |
+ 1, 1, 1, 1, 1, 1, 1, 2, | |
+ 1, 1, 1, 1, 1, 1, 2, 2, | |
+ 1, 1, 1, 1, 1, 2, 2, 3, | |
+ 1, 1, 1, 1, 2, 2, 3, 3, | |
+ 1, 1, 1, 2, 2, 3, 3, 3, | |
+ 1, 1, 2, 2, 3, 3, 3, 3], | |
+ [ 1, 1, 1, 2, 2, 3, 3, 3, | |
+ 1, 1, 1, 2, 3, 3, 3, 3, | |
+ 1, 1, 1, 3, 3, 3, 3, 3, | |
+ 2, 2, 3, 3, 3, 3, 3, 3, | |
+ 2, 3, 3, 3, 3, 3, 3, 3, | |
+ 3, 3, 3, 3, 3, 3, 3, 3, | |
+ 3, 3, 3, 3, 3, 3, 3, 3, | |
+ 3, 3, 3, 3, 3, 3, 3, 3] | |
+ ]}, | |
+ | |
+ 'low': {'subsampling': 2, # "4:1:1" | |
+ 'quantization': [ | |
+ [18, 14, 14, 21, 30, 35, 34, 17, | |
+ 14, 16, 16, 19, 26, 23, 12, 12, | |
+ 14, 16, 17, 21, 23, 12, 12, 12, | |
+ 21, 19, 21, 23, 12, 12, 12, 12, | |
+ 30, 26, 23, 12, 12, 12, 12, 12, | |
+ 35, 23, 12, 12, 12, 12, 12, 12, | |
+ 34, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12], | |
+ [20, 19, 22, 27, 20, 20, 17, 17, | |
+ 19, 25, 23, 14, 14, 12, 12, 12, | |
+ 22, 23, 14, 14, 12, 12, 12, 12, | |
+ 27, 14, 14, 12, 12, 12, 12, 12, | |
+ 20, 14, 12, 12, 12, 12, 12, 12, | |
+ 20, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12] | |
+ ]}, | |
+ 'medium': {'subsampling': 2, # "4:1:1" | |
+ 'quantization': [ | |
+ [12, 8, 8, 12, 17, 21, 24, 17, | |
+ 8, 9, 9, 11, 15, 19, 12, 12, | |
+ 8, 9, 10, 12, 19, 12, 12, 12, | |
+ 12, 11, 12, 21, 12, 12, 12, 12, | |
+ 17, 15, 19, 12, 12, 12, 12, 12, | |
+ 21, 19, 12, 12, 12, 12, 12, 12, | |
+ 24, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12], | |
+ [13, 11, 13, 16, 20, 20, 17, 17, | |
+ 11, 14, 14, 14, 14, 12, 12, 12, | |
+ 13, 14, 14, 14, 12, 12, 12, 12, | |
+ 16, 14, 14, 12, 12, 12, 12, 12, | |
+ 20, 14, 12, 12, 12, 12, 12, 12, | |
+ 20, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12, | |
+ 17, 12, 12, 12, 12, 12, 12, 12] | |
+ ]}, | |
+ | |
'high': {'subsampling': 0, # "4:4:4" | |
'quantization': [ | |
[ 6, 4, 4, 6, 9, 11, 12, 16, | |
@@ -123,27 +183,7 @@ | |
17, 12, 12, 12, 12, 12, 12, 12] | |
]}, | |
- 'web_high': {'subsampling': 0, # "4:4:4" | |
- 'quantization': [ | |
- [ 6, 4, 4, 6, 9, 11, 12, 16, | |
- 4, 5, 5, 6, 8, 10, 12, 12, | |
- 4, 5, 5, 6, 10, 12, 14, 19, | |
- 6, 6, 6, 11, 12, 15, 19, 28, | |
- 9, 8, 10, 12, 16, 20, 27, 31, | |
- 11, 10, 12, 15, 20, 27, 31, 31, | |
- 12, 12, 14, 19, 27, 31, 31, 31, | |
- 16, 12, 19, 28, 31, 31, 31, 31], | |
- [ 7, 7, 13, 24, 26, 31, 31, 31, | |
- 7, 12, 16, 21, 31, 31, 31, 31, | |
- 13, 16, 17, 31, 31, 31, 31, 31, | |
- 24, 21, 31, 31, 31, 31, 31, 31, | |
- 26, 31, 31, 31, 31, 31, 31, 31, | |
- 31, 31, 31, 31, 31, 31, 31, 31, | |
- 31, 31, 31, 31, 31, 31, 31, 31, | |
- 31, 31, 31, 31, 31, 31, 31, 31] | |
- ]}, | |
- | |
- 'web_very_high': {'subsampling': 0, # "4:4:4" | |
+ 'maximum': {'subsampling': 0, # "4:4:4" | |
'quantization': [ | |
[ 2, 2, 2, 2, 3, 4, 5, 6, | |
2, 2, 2, 2, 3, 4, 5, 6, | |
@@ -154,52 +194,12 @@ | |
5, 5, 7, 9, 12, 12, 12, 12, | |
6, 6, 9, 12, 12, 12, 12, 12], | |
[ 3, 3, 5, 9, 13, 15, 15, 15, | |
- 3, 4, 6, 11, 14, 12, 12, 12, | |
+ 3, 4, 6, 10, 14, 12, 12, 12, | |
5, 6, 9, 14, 12, 12, 12, 12, | |
- 9, 11, 14, 12, 12, 12, 12, 12, | |
+ 9, 10, 14, 12, 12, 12, 12, 12, | |
13, 14, 12, 12, 12, 12, 12, 12, | |
15, 12, 12, 12, 12, 12, 12, 12, | |
15, 12, 12, 12, 12, 12, 12, 12, | |
15, 12, 12, 12, 12, 12, 12, 12] | |
]}, | |
- | |
- 'web_low': {'subsampling': 2, # "4:1:1" | |
- 'quantization': [ | |
- [20, 16, 25, 39, 50, 46, 62, 68, | |
- 16, 18, 23, 38, 38, 53, 65, 68, | |
- 25, 23, 31, 38, 53, 65, 68, 68, | |
- 39, 38, 38, 53, 65, 68, 68, 68, | |
- 50, 38, 53, 65, 68, 68, 68, 68, | |
- 46, 53, 65, 68, 68, 68, 68, 68, | |
- 62, 65, 68, 68, 68, 68, 68, 68, | |
- 68, 68, 68, 68, 68, 68, 68, 68], | |
- [21, 25, 32, 38, 54, 68, 68, 68, | |
- 25, 28, 24, 38, 54, 68, 68, 68, | |
- 32, 24, 32, 43, 66, 68, 68, 68, | |
- 38, 38, 43, 53, 68, 68, 68, 68, | |
- 54, 54, 66, 68, 68, 68, 68, 68, | |
- 68, 68, 68, 68, 68, 68, 68, 68, | |
- 68, 68, 68, 68, 68, 68, 68, 68, | |
- 68, 68, 68, 68, 68, 68, 68, 68] | |
- ]}, | |
- | |
- 'low': {'subsampling': 2, # "4:1:1" | |
- 'quantization': [ | |
- [18, 14, 14, 21, 30, 35, 34, 17, | |
- 14, 16, 16, 19, 26, 23, 12, 12, | |
- 14, 16, 17, 21, 23, 12, 12, 12, | |
- 21, 19, 21, 23, 12, 12, 12, 12, | |
- 30, 26, 23, 12, 12, 12, 12, 12, | |
- 35, 23, 12, 12, 12, 12, 12, 12, | |
- 34, 12, 12, 12, 12, 12, 12, 12, | |
- 17, 12, 12, 12, 12, 12, 12, 12], | |
- [20, 19, 22, 27, 20, 20, 17, 17, | |
- 19, 25, 23, 14, 14, 12, 12, 12, | |
- 22, 23, 14, 14, 12, 12, 12, 12, | |
- 27, 14, 14, 12, 12, 12, 12, 12, | |
- 20, 14, 12, 12, 12, 12, 12, 12, | |
- 20, 12, 12, 12, 12, 12, 12, 12, | |
- 17, 12, 12, 12, 12, 12, 12, 12, | |
- 17, 12, 12, 12, 12, 12, 12, 12] | |
- ]} | |
} | |
\ No newline at end of file | |
# HG changeset patch | |
# User etienne <[email protected]> | |
# Date 1327679927 18000 | |
# Node ID bb153aafa59b1d95c5df30f0e611d5aa1cac26b6 | |
# Parent 5462f071739bb29df0d7ceede421ba19a7d14eb1 | |
Extracted get_sampling and convert_dict_qtables functions from the JPEG _save function, so they can be use to inspect JPEG and build presets more easily. | |
diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py | |
--- a/PIL/JpegImagePlugin.py | |
+++ b/PIL/JpegImagePlugin.py | |
@@ -434,6 +434,22 @@ | |
21, 34, 37, 47, 50, 56, 59, 61, | |
35, 36, 48, 49, 57, 58, 62, 63) | |
+ | |
+def convert_dict_qtables(qtables): | |
+ qtables = [qtables[key] for key in xrange(len(qtables)) if qtables.has_key(key)] | |
+ for idx, table in enumerate(qtables): | |
+ qtables[idx] = [table[i] for i in zigzag_index] | |
+ return qtables | |
+ | |
+def get_sampling(im): | |
+ samplings = { | |
+ (1, 1, 1, 1, 1, 1): 0, | |
+ (2, 1, 1, 1, 1, 1): 1, | |
+ (2, 2, 1, 1, 1, 1): 2, | |
+ } | |
+ sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] | |
+ return samplings.get(sampling, -1) | |
+ | |
def _save(im, fp, filename): | |
try: | |
@@ -466,16 +482,6 @@ | |
if qtables in presets: | |
qtables = presets[qtables].get('quantization') | |
- def getsampling(im): | |
- samplings = { | |
- (1, 1, 1, 1, 1, 1): 0, | |
- (2, 1, 1, 1, 1, 1): 1, | |
- (2, 2, 1, 1, 1, 1): 2, | |
- } | |
- layer = im.layer | |
- sampling = layer[0][1:3] + layer[1][1:3] + layer[2][1:3] | |
- return samplings.get(sampling, -1) | |
- | |
if subsampling == "4:4:4": | |
subsampling = 0 | |
elif subsampling == "4:2:2": | |
@@ -485,7 +491,7 @@ | |
elif subsampling == "keep": | |
if im.format != "JPEG": | |
raise ValueError("Cannot use 'keep' when original image is not a JPEG") | |
- subsampling = getsampling(im) | |
+ subsampling = get_sampling(im) | |
def validate_qtables(qtables): | |
if qtables is None: | |
@@ -500,9 +506,7 @@ | |
qtables = [lines[s:s+64] for s in xrange(0, len(lines), 64)] | |
if isinstance(qtables, (tuple, list, dict)): | |
if isinstance(qtables, dict): | |
- qtables = [qtables[key] for key in xrange(len(qtables)) if qtables.has_key(key)] | |
- for idx, table in enumerate(qtables): | |
- qtables[idx] = [table[i] for i in zigzag_index] | |
+ qtables = convert_dict_qtables(qtables) | |
elif isinstance(qtables, tuple): | |
qtables = list(qtables) | |
if not (0 < len(qtables) < 5): | |
# HG changeset patch | |
# User etienne <[email protected]> | |
# Date 1327680655 18000 | |
# Node ID d6f15d93350d5dd2258669c8f37088e6a1e5d565 | |
# Parent bb153aafa59b1d95c5df30f0e611d5aa1cac26b6 | |
Added documentation on JPEG quality presets. | |
diff --git a/PIL/JpegPresets.py b/PIL/JpegPresets.py | |
--- a/PIL/JpegPresets.py | |
+++ b/PIL/JpegPresets.py | |
@@ -20,7 +20,52 @@ | |
Example: | |
im.save("image_name.jpg", quality="web_high") | |
+ | |
+ | |
+Subsampling | |
+----------- | |
+ | |
+Subsampling is the practice of encoding images by implementing less resolution | |
+for chroma information than for luma information. | |
+(ref.: http://en.wikipedia.org/wiki/Chroma_subsampling) | |
+Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and | |
+4:1:1 (or 4:2:0?). | |
+ | |
+You can get the subsampling of a JPEG with the | |
+`JpegImagePlugin.get_subsampling(im)` function. | |
+ | |
+ | |
+Quantization tables | |
+------------------- | |
+ | |
+They are values use by the DCT (Discrete cosine transform) to remove | |
+*unnecessary* information from the image (the lossy part of the compression). | |
+(ref.: http://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, | |
+ http://en.wikipedia.org/wiki/JPEG#Quantization) | |
+ | |
+You can get the quantization tables of a JPEG with: | |
+ | |
+ im.quantization | |
+ | |
+This will return a dict with a number of arrays. You can pass this dict directly | |
+as the qtables argument when saving a JPEG. | |
+ | |
+The tables format between im.quantization and quantization in presets differ in | |
+3 ways: | |
+ | |
+ 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 | |
+`JpegImagePlugin.convert_dict_qtables(dict_qtables)` function. | |
+ | |
+Libjpeg ref.: http://www.jpegcameras.com/libjpeg/libjpeg-3.html | |
+ | |
""" | |
presets = { | |
# HG changeset patch | |
# User etienne <[email protected]> | |
# Date 1327688188 18000 | |
# Node ID 55d24e80a982b6180464d6bf1125e021b4b0cbf1 | |
# Parent d6f15d93350d5dd2258669c8f37088e6a1e5d565 | |
Fixed compilation warnings in JPEG get_qtables_arrays in encode.c. | |
diff --git a/encode.c b/encode.c | |
--- a/encode.c | |
+++ b/encode.c | |
@@ -590,7 +590,8 @@ | |
qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int)); | |
if (!qarrays) { | |
Py_DECREF(tables); | |
- return PyErr_NoMemory(); | |
+ PyErr_NoMemory(); | |
+ return NULL; | |
} | |
for (i = 0; i < num_tables; i++) { | |
table = PySequence_Fast_GET_ITEM(tables, i); | |
@@ -608,7 +609,8 @@ | |
qarrays[i] = (unsigned int*) PyMem_Malloc(DCTSIZE2 * sizeof(unsigned int)); | |
if (!qarrays[i]) { | |
Py_DECREF(tables); | |
- return PyErr_NoMemory(); | |
+ PyErr_NoMemory(); | |
+ return NULL; | |
} | |
for (j = 0; j < DCTSIZE2; j++) { | |
qarrays[i][j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment