Merge pull request #4440 from radarhere/jpeg_quality

Allow saving of zero quality JPEG images
This commit is contained in:
Hugo van Kemenade 2020-02-25 12:02:53 +02:00 committed by GitHub
commit 3c995fd173
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 29 additions and 9 deletions

View File

@ -299,6 +299,10 @@ class TestFileJpeg(PillowTestCase):
assert_image(im1, im2.mode, im2.size) assert_image(im1, im2.mode, im2.size)
assert im1.bytes >= im2.bytes assert im1.bytes >= im2.bytes
im3 = self.roundtrip(hopper(), quality=0)
assert_image(im1, im3.mode, im3.size)
self.assertGreater(im2.bytes, im3.bytes)
def test_smooth(self): def test_smooth(self):
im1 = self.roundtrip(hopper()) im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), smooth=100) im2 = self.roundtrip(hopper(), smooth=100)

View File

@ -302,7 +302,7 @@ The :py:meth:`~PIL.Image.Image.open` method may set the following
The :py:meth:`~PIL.Image.Image.save` method supports the following options: The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**quality** **quality**
The image quality, on a scale from 1 (worst) to 95 (best). The default is The image quality, on a scale from 0 (worst) to 95 (best). The default is
75. Values above 95 should be avoided; 100 disables portions of the JPEG 75. Values above 95 should be avoided; 100 disables portions of the JPEG
compression algorithm, and results in large files with hardly any gain in compression algorithm, and results in large files with hardly any gain in
image quality. image quality.

View File

@ -0,0 +1,16 @@
7.0.0
-----
Allow saving of zero quality JPEG images
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If no quality was specified when saving a JPEG, Pillow internally used a value
of zero to indicate that the default quality should be used. However, this
removed the ability to actually save a JPEG with zero quality. This has now
been resolved.
.. code-block:: python
from PIL import Image
im = Image.open("hopper.jpg")
im.save("out.jpg", quality=0)

View File

@ -616,17 +616,17 @@ def _save(im, fp, filename):
dpi = [round(x) for x in info.get("dpi", (0, 0))] dpi = [round(x) for x in info.get("dpi", (0, 0))]
quality = info.get("quality", 0) quality = info.get("quality", -1)
subsampling = info.get("subsampling", -1) subsampling = info.get("subsampling", -1)
qtables = info.get("qtables") qtables = info.get("qtables")
if quality == "keep": if quality == "keep":
quality = 0 quality = -1
subsampling = "keep" subsampling = "keep"
qtables = "keep" qtables = "keep"
elif quality in presets: elif quality in presets:
preset = presets[quality] preset = presets[quality]
quality = 0 quality = -1
subsampling = preset.get("subsampling", -1) subsampling = preset.get("subsampling", -1)
qtables = preset.get("quantization") qtables = preset.get("quantization")
elif not isinstance(quality, int): elif not isinstance(quality, int):
@ -749,8 +749,8 @@ def _save(im, fp, filename):
# CMYK can be bigger # CMYK can be bigger
if im.mode == "CMYK": if im.mode == "CMYK":
bufsize = 4 * im.size[0] * im.size[1] bufsize = 4 * im.size[0] * im.size[1]
# keep sets quality to 0, but the actual value may be high. # keep sets quality to -1, but the actual value may be high.
elif quality >= 95 or quality == 0: elif quality >= 95 or quality == -1:
bufsize = 2 * im.size[0] * im.size[1] bufsize = 2 * im.size[0] * im.size[1]
else: else:
bufsize = im.size[0] * im.size[1] bufsize = im.size[0] * im.size[1]

View File

@ -67,7 +67,7 @@ typedef struct {
/* CONFIGURATION */ /* CONFIGURATION */
/* Quality (1-100, 0 means default) */ /* Quality (0-100, -1 means default) */
int quality; int quality;
/* Progressive mode */ /* Progressive mode */

View File

@ -153,7 +153,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
int i; int i;
int quality = 100; int quality = 100;
int last_q = 0; int last_q = 0;
if (context->quality > 0) { if (context->quality != -1) {
quality = context->quality; quality = context->quality;
} }
for (i = 0; i < context->qtablesLen; i++) { for (i = 0; i < context->qtablesLen; i++) {
@ -171,7 +171,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
for (i = last_q; i < context->cinfo.num_components; i++) { for (i = last_q; i < context->cinfo.num_components; i++) {
context->cinfo.comp_info[i].quant_tbl_no = last_q; context->cinfo.comp_info[i].quant_tbl_no = last_q;
} }
} else if (context->quality > 0) { } else if (context->quality != -1) {
jpeg_set_quality(&context->cinfo, context->quality, 1); jpeg_set_quality(&context->cinfo, context->quality, 1);
} }