Pillow icon indicating copy to clipboard operation
Pillow copied to clipboard

UnidentifiedImageError opening simple PSD using shapes and masks

Open ppearsonhutch opened this issue 1 year ago • 8 comments

What did you do?

Attempted to open the supplied psd image with PIL.open() in python.

I didn't actually create this image myself, nor can I figure out what exactly is wrong with it, but I've managed to reduce the cause of the issue down to 2 shape layers, one with a layer mask. Removing either layer fixes the error. Additionally, inserting a new empty layer between the two layers also fixes the error. So it's not critical and I can work around it, but I have a feeling it's a bug in the psd opening logic if it's somehow fixed by adding blank layers.

Image below of the layers: Screenshot 2024-04-30 at 21 34 44

What did you expect to happen?

No PIL.UnidentifiedImageError when opening the PSD.

What actually happened?

Received a PIL.UnidentifiedImageError when opening the PSD with PIL.open()

What are your OS, Python and Pillow versions?

  • OS: MacOS Sonoma 14.4.1
  • Python: 3.9.6 (via shim/pyenv)
  • Pillow: 10.3.0 (via pip)
--------------------------------------------------------------------
Pillow 10.3.0
Python 3.9.6 (default, Apr  6 2024, 22:52:34)
       [Clang 15.0.0 (clang-1500.3.9.4)]
--------------------------------------------------------------------
Python executable is /Users/<username>/.pyenv/versions/3.9.6/bin/python3
System Python files loaded from /Users/<username>/.pyenv/versions/3.9.6
--------------------------------------------------------------------
Python Pillow modules loaded from /Users/<username>/.pyenv/versions/3.9.6/lib/python3.9/site-packages/PIL
Binary Pillow modules loaded from /Users/<username>/.pyenv/versions/3.9.6/lib/python3.9/site-packages/PIL
--------------------------------------------------------------------
--- PIL CORE support ok, compiled for 10.3.0
--- TKINTER support ok, loaded 8.6
--- FREETYPE2 support ok, loaded 2.13.2
--- LITTLECMS2 support ok, loaded 2.16
--- WEBP support ok, loaded 1.3.2
--- WEBP Transparency support ok
--- WEBPMUX support ok
--- WEBP Animation support ok
--- JPEG support ok, compiled for libjpeg-turbo 3.0.2
--- OPENJPEG (JPEG2000) support ok, loaded 2.5.2
--- ZLIB (PNG/ZIP) support ok, loaded 1.3.1
--- LIBTIFF support ok, loaded 4.6.0
*** RAQM (Bidirectional Text) support not installed
*** LIBIMAGEQUANT (Quantization method) support not installed
--- XCB (X protocol) support ok
--------------------------------------------------------------------

Image is attached as zip because github doesn't allow psd attachments. ExamplePSD.psd.zip (29kb)

Code to reproduce (simple open PSD script):

import sys
import PIL
from PIL import Image

def open_psd(psd_file_path):
    # Open the PSD file
    psd_image = Image.open(psd_file_path)
    
if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python3 piltest.py <input_psd_path>")
    else:
        psd_file_path = sys.argv[1]
        open_psd(psd_file_path)

ppearsonhutch avatar Apr 30 '24 09:04 ppearsonhutch

Removing either layer fixes the error. Additionally, inserting a new empty layer between the two layers also fixes the error.

Would it be an easy task for you to attach these variations as well for comparison?

radarhere avatar Apr 30 '24 09:04 radarhere

No problem at all. Here's four variants. I've included in each zip a screen clipping of what the layers look like in photoshop. All the following .psd files open successfully with PIL.

ExamplePSDWithBottomLayerMaskRemoved.zip

ExamplePSDWithTopMostLayerRemoved.zip

ExamplePSDWithEmptyLayerAdded.zip

ExamplePSDWithBottomLayerRemoved.zip

ppearsonhutch avatar Apr 30 '24 10:04 ppearsonhutch

So, the specification states that

If the compression code is 1, the image data starts with the byte counts for all the scan lines in the channel (LayerBottom-LayerTop)

and as far as I can see, the byte counts in the first image become incorrect at the end, or end prematurely.

However, your code is only trying to open the image. You are not trying to access the PSD layers. I have created #8039 to delay parsing the layers until the user needs Pillow to do so, and with that, your image can be opened successfully - you just can't extract the layers individually.

radarhere avatar May 02 '24 02:05 radarhere

Would that be sufficient to resolve this, or do you think that the image is valid and further investigation should be done into reading the layers?

radarhere avatar May 04 '24 00:05 radarhere

Apologies for the delay replying. Thanks for the fix! I do actually intend to access the layers later in my use case, so I'm not sure this fix is sufficient for my usage.

I'm pretty confident this image is valid, as I can open photoshop and create a similar image using the same layer format easily and get the same error. However I can easily fix these images by hand so any further fix is not urgently required.

ppearsonhutch avatar May 13 '24 10:05 ppearsonhutch

If we were to figure out the error, then the 'blue 1' layer would be readable.

However, the 'blue' layer would still not be, as Pillow doesn't currently support reading layers with user supplied masks. If you're interested in accessing that as well, then this is a feature request - that doesn't change anything really, just letting you know it may be more difficult than you expected.

radarhere avatar May 18 '24 08:05 radarhere

I appreciate this is probably quite tricky given the complexity of PSDs and layers.

I have done a bit of experimenting and realised that I am able to access the layer information by using a second library psd_tools which means the fix you've implemented so far does resolve the issue I was encountering. I can simply use Pillow to do the image conversion and psd_tools to access the layer information.

Thanks again for your help so far.

ppearsonhutch avatar May 23 '24 08:05 ppearsonhutch

Pillow 10.4.0 has just been released with https://github.com/python-pillow/Pillow/pull/8039

radarhere avatar Jul 01 '24 10:07 radarhere