Repository: python-pillow/Pillow

Issue:
Can't produce a Quality 0 JPEG
<!--
Thank you for reporting an issue.

Follow these guidelines to ensure your issue is handled properly.

If you have a ...

1.  General question: consider asking the question on Stack Overflow
    with the python-imaging-library tag:

    * https://stackoverflow.com/questions/tagged/python-imaging-library

    Do not ask a question in both places.

    If you think you have found a bug or have an unexplained exception
    then file a bug report here.

2.  Bug report: include a self-contained, copy-pastable example that
    generates the issue if possible. Be concise with code posted.
    Guidelines on how to provide a good bug report:

    * https://stackoverflow.com/help/mcve

    Bug reports which follow these guidelines are easier to diagnose,
    and are often handled much more quickly.

3.  Feature request: do a quick search of existing issues
    to make sure this has not been asked before.

We know asking good questions takes effort, and we appreciate your time.
Thank you.
-->

### What did you do?

```
im.save(output_path / i.with_suffix('.jpg').name, quality=0)
```

### What did you expect to happen?

A JPEG with libjpeg quantization equivalent to zero quality to be output.

### What actually happened?

A JPEG with libjpeg quantization equivalent to quality 75 was output

### What are your OS, Python and Pillow versions?

* OS: Linux
* Python: 3.5
* Pillow: 

###  Comments

I do research in image compression and as part of that research I wanted to produce some quality 0 JPEG files, the quantization matrix for that should filled with 255, dumping the resulting JPEGs quantization tables with `djpeg` I see that they are quality 75, and from the source code it looks like `quality=0` indicates to use the defaulty quality which is 75. 

Quality zero is a completely valid quality for libjpeg, but it doesn't work in Pillow. I'm not sure how many people care about quality 0 JPEGs so it might not be worth fixing but I still figured I'd leave this here.


Discussion:
I've created PR #4440 to resolve this.

Resolving patch:
diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py
--- a/src/PIL/JpegImagePlugin.py
+++ b/src/PIL/JpegImagePlugin.py
@@ -616,17 +616,17 @@ def _save(im, fp, filename):
 
     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)
     qtables = info.get("qtables")
 
     if quality == "keep":
-        quality = 0
+        quality = -1
         subsampling = "keep"
         qtables = "keep"
     elif quality in presets:
         preset = presets[quality]
-        quality = 0
+        quality = -1
         subsampling = preset.get("subsampling", -1)
         qtables = preset.get("quantization")
     elif not isinstance(quality, int):
@@ -749,8 +749,8 @@ def validate_qtables(qtables):
         # CMYK can be bigger
         if im.mode == "CMYK":
             bufsize = 4 * im.size[0] * im.size[1]
-        # keep sets quality to 0, but the actual value may be high.
-        elif quality >= 95 or quality == 0:
+        # keep sets quality to -1, but the actual value may be high.
+        elif quality >= 95 or quality == -1:
             bufsize = 2 * im.size[0] * im.size[1]
         else:
             bufsize = im.size[0] * im.size[1]


Test patch:
diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py
--- a/Tests/test_file_jpeg.py
+++ b/Tests/test_file_jpeg.py
@@ -299,6 +299,10 @@ def test_quality(self):
         assert_image(im1, im2.mode, im2.size)
         self.assertGreaterEqual(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):
         im1 = self.roundtrip(hopper())
         im2 = self.roundtrip(hopper(), smooth=100)