Repository: python-pillow/Pillow

Issue:
PIL.UnidentifiedImageError on TIFF file (regression from 9.0.1 to 9.1.0)
### What did you do?

The attached TIFF file, which was generated by [Hugin](http://hugin.sourceforge.net/), fails to open in Pillow 9.1.0. It works fine in 9.0.1 and prior versions.

[example.zip](https://github.com/python-pillow/Pillow/files/8612938/example.zip)

### What did you expect to happen?

With 9.0.1:
```pycon
>>> import PIL
>>> PIL.__version__
'9.0.1'
>>> from PIL import Image
>>> Image.open('eqr.tiff')
<PIL.TiffImagePlugin.TiffImageFile image mode=RGBA size=512x256 at 0x1073FC4C0>
```

### What actually happened?

With 9.1.0:
```pycon
>>> import PIL
>>> PIL.__version__
'9.1.0'
>>> from PIL import Image
>>> Image.open('eqr.tiff')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/private/tmp/test/lib/python3.9/site-packages/PIL/Image.py", line 3123, in open
    raise UnidentifiedImageError(
PIL.UnidentifiedImageError: cannot identify image file 'eqr.tiff'
```

### What are your OS, Python and Pillow versions?

* OS: Tested on macOS and Linux
* Python: Tested on 3.9.10 and 3.9.12
* Pillow: 9.1.0


Discussion:
`git bisect` points to bb5a090f60ed08941e5bdd7077f9a53caec99b57
If I run [`tiffinfo`](https://linux.die.net/man/1/tiffinfo) on your file, I receive a warning

> TIFFReadDirectory: Warning, Sum of Photometric type-related color channels and ExtraSamples doesn't match SamplesPerPixel. Defining non-color channels as ExtraSamples..

Just as an explanation, Pillow is failing to read the image for the same reason - the SampesPerPixel value doesn't match the rest of the data.

Resolving patch:
diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py
--- a/src/PIL/TiffImagePlugin.py
+++ b/src/PIL/TiffImagePlugin.py
@@ -1355,19 +1355,19 @@ def _setup(self):
             bps_count = 1
         bps_count += len(extra_tuple)
         bps_actual_count = len(bps_tuple)
-        if bps_count < bps_actual_count:
+        samples_per_pixel = self.tag_v2.get(
+            SAMPLESPERPIXEL,
+            3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1,
+        )
+        if samples_per_pixel < bps_actual_count:
             # If a file has more values in bps_tuple than expected,
             # remove the excess.
-            bps_tuple = bps_tuple[:bps_count]
-        elif bps_count > bps_actual_count and bps_actual_count == 1:
+            bps_tuple = bps_tuple[:samples_per_pixel]
+        elif samples_per_pixel > bps_actual_count and bps_actual_count == 1:
             # If a file has only one value in bps_tuple, when it should have more,
             # presume it is the same number of bits for all of the samples.
-            bps_tuple = bps_tuple * bps_count
+            bps_tuple = bps_tuple * samples_per_pixel
 
-        samples_per_pixel = self.tag_v2.get(
-            SAMPLESPERPIXEL,
-            3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1,
-        )
         if len(bps_tuple) != samples_per_pixel:
             raise SyntaxError("unknown data organization")
 


Test patch:
diff --git a/Tests/images/tiff_wrong_bits_per_sample_3.tiff b/Tests/images/tiff_wrong_bits_per_sample_3.tiff
new file mode 100644
Binary files /dev/null and b/Tests/images/tiff_wrong_bits_per_sample_3.tiff differ
diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py
--- a/Tests/test_file_tiff.py
+++ b/Tests/test_file_tiff.py
@@ -92,17 +92,33 @@ def test_bigtiff(self):
             assert_image_equal_tofile(im, "Tests/images/hopper.tif")
 
     @pytest.mark.parametrize(
-        "file_name,mode,size,offset",
+        "file_name,mode,size,tile",
         [
-            ("tiff_wrong_bits_per_sample.tiff", "RGBA", (52, 53), 160),
-            ("tiff_wrong_bits_per_sample_2.tiff", "RGB", (16, 16), 8),
+            (
+                "tiff_wrong_bits_per_sample.tiff",
+                "RGBA",
+                (52, 53),
+                [("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))],
+            ),
+            (
+                "tiff_wrong_bits_per_sample_2.tiff",
+                "RGB",
+                (16, 16),
+                [("raw", (0, 0, 16, 16), 8, ("RGB", 0, 1))],
+            ),
+            (
+                "tiff_wrong_bits_per_sample_3.tiff",
+                "RGBA",
+                (512, 256),
+                [("libtiff", (0, 0, 512, 256), 0, ("RGBA", "tiff_lzw", False, 48782))],
+            ),
         ],
     )
-    def test_wrong_bits_per_sample(self, file_name, mode, size, offset):
+    def test_wrong_bits_per_sample(self, file_name, mode, size, tile):
         with Image.open("Tests/images/" + file_name) as im:
             assert im.mode == mode
             assert im.size == size
-            assert im.tile == [("raw", (0, 0) + size, offset, (mode, 0, 1))]
+            assert im.tile == tile
             im.load()
 
     def test_set_legacy_api(self):