Repository: python-pillow/Pillow

Issue:
Save fails after converting 16-bit RGB to grayscale
### What did you do?
Downloaded test-PNG from "pngsuite" (see below), converted to grayscale using the matrix-option, saved the result.

### What did you expect to happen?
that the script would succeed like with almost all other PNGs from "pngsuite"

### What actually happened?
Save fails with

```
Traceback (most recent call last):
  File "C:/base2_nl/py-grayscale/minimalExampleBug.py", line 5, in <module>
    im.save('tbbn2c16-out.png')
  File "C:\Python36\lib\site-packages\PIL\Image.py", line 1935, in save
    save_handler(self, fp, filename)
  File "C:\Python36\lib\site-packages\PIL\PngImagePlugin.py", line 790, in _save
    transparency = max(0, min(65535, transparency))
TypeError: '<' not supported between instances of 'tuple' and 'int'
```

### What versions of Pillow and Python are you using?
Pillow 5.1.0
Python 3.6.5

### Minimal example to reproduce the error
```python
from PIL import Image

im = Image.open('tbbn2c16.png') # from here: http://www.schaik.com/pngsuite/tbbn2c16.png
im = im.convert("L",  (0.22, 0.72, 0.06, 0))
im.save('tbbn2c16-out.png') # fails
```

BTW in this case transparency is a tuple with the following value before the exception:
```python
transparency  = (65535,65535,65535)
```

Probably line 790 would have produced the expected result in Python 2.7, but is invalid in Python 3.x when mixing integers and tuple parameters.




Discussion:

Resolving patch:
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -900,12 +900,28 @@ def convert(self, mode=None, matrix=None, dither=None,
         if not mode or (mode == self.mode and not matrix):
             return self.copy()
 
+        has_transparency = self.info.get('transparency') is not None
         if matrix:
             # matrix conversion
             if mode not in ("L", "RGB"):
                 raise ValueError("illegal conversion")
             im = self.im.convert_matrix(mode, matrix)
-            return self._new(im)
+            new = self._new(im)
+            if has_transparency and self.im.bands == 3:
+                transparency = new.info['transparency']
+
+                def convert_transparency(m, v):
+                    v = m[0]*v[0] + m[1]*v[1] + m[2]*v[2] + m[3]*0.5
+                    return max(0, min(255, int(v)))
+                if mode == "L":
+                    transparency = convert_transparency(matrix, transparency)
+                elif len(mode) == 3:
+                    transparency = tuple([
+                        convert_transparency(matrix[i*4:i*4+4], transparency)
+                        for i in range(0, len(transparency))
+                    ])
+                new.info['transparency'] = transparency
+            return new
 
         if mode == "P" and self.mode == "RGBA":
             return self.quantize(colors)
@@ -913,8 +929,7 @@ def convert(self, mode=None, matrix=None, dither=None,
         trns = None
         delete_trns = False
         # transparency handling
-        if "transparency" in self.info and \
-                self.info['transparency'] is not None:
+        if has_transparency:
             if self.mode in ('L', 'RGB') and mode == 'RGBA':
                 # Use transparent conversion to promote from transparent
                 # color to an alpha channel.


Test patch:
diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py
--- a/Tests/test_image_convert.py
+++ b/Tests/test_image_convert.py
@@ -187,6 +187,7 @@ def test_matrix_xyz(self):
         def matrix_convert(mode):
             # Arrange
             im = hopper('RGB')
+            im.info['transparency'] = (255, 0, 0)
             matrix = (
                 0.412453, 0.357580, 0.180423, 0,
                 0.212671, 0.715160, 0.072169, 0,
@@ -203,9 +204,11 @@ def matrix_convert(mode):
             target = Image.open('Tests/images/hopper-XYZ.png')
             if converted_im.mode == 'RGB':
                 self.assert_image_similar(converted_im, target, 3)
+                self.assertEqual(converted_im.info['transparency'], (105, 54, 4))
             else:
                 self.assert_image_similar(converted_im,
                                           target.getchannel(0), 1)
+                self.assertEqual(converted_im.info['transparency'], 105)
 
         matrix_convert('RGB')
         matrix_convert('L')