Skip to content

Instantly share code, notes, and snippets.

@etienned
Created March 6, 2013 18:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save etienned/5101740 to your computer and use it in GitHub Desktop.
Save etienned/5101740 to your computer and use it in GitHub Desktop.
Patch to add quantization support to JPEG in PIL.
# HG changeset patch
# User etienne <etienne.desautels@gmail.com>
# 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 <etienne.desautels@gmail.com>
# 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 <etienne.desautels@gmail.com>
# 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 <etienne.desautels@gmail.com>
# 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 <etienne.desautels@gmail.com>
# 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 <etienne.desautels@gmail.com>
# 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 <etienne.desautels@gmail.com>
# 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 <etienne.desautels@gmail.com>
# 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 <etienne.desautels@gmail.com>
# 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 <etienne.desautels@gmail.com>
# 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