Coder Social home page Coder Social logo

cgohlke / psdtags Goto Github PK

View Code? Open in Web Editor NEW
17.0 5.0 3.0 360 KB

Read and write layered TIFF ImageSourceData and ImageResources tags

Home Page: https://pypi.org/project/psdtags

License: BSD 3-Clause "New" or "Revised" License

Python 100.00%
python photoshop psd tiff format-reader

psdtags's People

Contributors

cgohlke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

psdtags's Issues

FR: add tests that assert equality of bytestreams that take a roundtrip through psdtags with unknown=False

I think it would be beneficial to add tests that check for non-determinism and/or accidental misparsing/mutation. If not at the highest/largest levels, at least for smaller primitives like PsdLayer etc, using existing test inputs.

I did this manually and fell down a rabbit hole unearthing what seems to be issue #10. I think such a test feature would likely catch small and large regressions across the codebase, either retroactively or in the future.

That being said, I don't have a sufficient command of the Adobe spec to know as to whether there are situations where non-determinism is encouraged (!!), nor of your codebase to know if determinism and repeatability are inherit goals. So please forgive me if I'm quite offbase here.

Thank you!
-Andrew

Some tif files failed to show layers info

Hi,
With some (rare) tif files I have an issue with getting layers info:

Traceback (most recent call last):
  File "C:\Users\yuriy.l\AppData\Local\Autodesk\3dsMax\2021 - 64bit\ENU\scripts\fraglab_asset_checker_libs\psdtags\psdtags.py", line 263, in __call__
    c = enum.EnumMeta.__call__(cls, *args, **kwds)
  File "C:\Program Files\Autodesk\3ds Max 2021\Python37\lib\enum.py", line 310, in __call__
    return cls.__new__(cls, value)
  File "C:\Program Files\Autodesk\3ds Max 2021\Python37\lib\enum.py", line 564, in __new__
    raise exc
  File "C:\Program Files\Autodesk\3ds Max 2021\Python37\lib\enum.py", line 548, in __new__
    result = cls._missing_(value)
  File "C:\Program Files\Autodesk\3ds Max 2021\Python37\lib\enum.py", line 577, in _missing_
    raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: b'iOpa' is not a valid PsdKey

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\yuriy.l\AppData\Local\Autodesk\3dsMax\2021 - 64bit\ENU\scripts\fraglab_asset_checker_libs\psdtags\psdtags.py", line 2967, in fromtiff
    data, name=os.path.split(filename)[-1], unknown=unknown
  File "C:\Users\yuriy.l\AppData\Local\Autodesk\3dsMax\2021 - 64bit\ENU\scripts\fraglab_asset_checker_libs\psdtags\psdtags.py", line 2951, in frombytes
    self = cls.read(fh, name=name, unknown=unknown)
  File "C:\Users\yuriy.l\AppData\Local\Autodesk\3dsMax\2021 - 64bit\ENU\scripts\fraglab_asset_checker_libs\psdtags\psdtags.py", line 2912, in read
    fh, psdformat, key, length=size, unknown=unknown
  File "C:\Users\yuriy.l\AppData\Local\Autodesk\3dsMax\2021 - 64bit\ENU\scripts\fraglab_asset_checker_libs\psdtags\psdtags.py", line 945, in read
    layers.append(PsdLayer.read(fh, psdformat, unknown=unknown))
  File "C:\Users\yuriy.l\AppData\Local\Autodesk\3dsMax\2021 - 64bit\ENU\scripts\fraglab_asset_checker_libs\psdtags\psdtags.py", line 1122, in read
    fh, psdformat, length=end - fh.tell(), unknown=unknown, align=2
  File "C:\Users\yuriy.l\AppData\Local\Autodesk\3dsMax\2021 - 64bit\ENU\scripts\fraglab_asset_checker_libs\psdtags\psdtags.py", line 3333, in read_psdtags
    key = PsdKey(fh.read(4))
  File "C:\Users\yuriy.l\AppData\Local\Autodesk\3dsMax\2021 - 64bit\ENU\scripts\fraglab_asset_checker_libs\psdtags\psdtags.py", line 265, in __call__
    raise exc
  File "C:\Users\yuriy.l\AppData\Local\Autodesk\3dsMax\2021 - 64bit\ENU\scripts\fraglab_asset_checker_libs\psdtags\psdtags.py", line 257, in __call__
    c = enum.EnumMeta.__call__(cls, *args, **kwds)
  File "C:\Program Files\Autodesk\3ds Max 2021\Python37\lib\enum.py", line 310, in __call__
    return cls.__new__(cls, value)
  File "C:\Program Files\Autodesk\3ds Max 2021\Python37\lib\enum.py", line 564, in __new__
    raise exc
  File "C:\Program Files\Autodesk\3ds Max 2021\Python37\lib\enum.py", line 548, in __new__
    result = cls._missing_(value)
  File "C:\Program Files\Autodesk\3ds Max 2021\Python37\lib\enum.py", line 577, in _missing_
    raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: b'apOi' is not a valid PsdKey

Please check the attached tif file:
power_stone_core_d_ddna.zip

I'm executing Python 3.7.6 from MaxScript inside 3ds Max 2021
The next line raises the exception:
imageSourceData = psdTagsLibrary.TiffImageSourceData.fromtiff(texturePath)

psdtags ver. 2022.8.25

write .tif to be read with photoshop

Is it possible to put multiple images, as layers, into a tif file and have them be read by photoshop.

I tried the following,

with tifffile.TiffWriter('temp.tif') as tiff:
    for img in data:
        data_block = bytes('Adobe Photoshop Document Data Block', 'ascii') + b'\x00'
        bim = bytes('8BIM', 'ascii')
        layer = bytes('Layr', 'ascii')
        img_bytes = img.tobytes()
        img_len = len(img_bytes)
        img_len_bytes = bytes(str(img_len), 'ascii')
        all_bytes = data_block + bim + layer + img_len_bytes
        byte_length = len(all_bytes) - len(data_block)
        bytes_bytes = bytes(byte_length)
        allll = all_bytes + bytes_bytes
        metad = {"37724": str(allll)}
        
        tiff.save(img, metadata=metad)
tif.close()

which works with preview (on mac) but not photoshop. Photoshop only sees the first layer...

Create multi-layered tiff from png images

Hi @cgohlke. First of all, awesome work!
I got your library from stackoverflow that it might be helpful for me.
What I need is to create a multi-layered tiff. Just the same like you exported tiff from your photoshop.

Here is an example of files: google drive
where you have photoshop file and tif created from it and also images which was created from.

This I want to accomplish, when you open generated tif, photoshop needs to see as layers.
e4542

I tried multiple approach:

  • generate psd and then with imagemagic convert to tif but without a success.
  • generate a tif with wand library
  • generate psd and convert with online tools
    ... and I'm complete stuck.

here is the stackoverflow

ValueError: operands could not be broadcast together with shapes

I'm trying to create two layer tiff from two png with the script below:

import numpy
import imagecodecs
import tifffile

from psdtags import (
    __version__,
    PsdBlendMode,
    PsdChannel,
    PsdChannelId,
    PsdClippingType,
    PsdColorSpaceType,
    PsdCompressionType,
    PsdEmpty,
    PsdFilterMask,
    PsdFormat,
    PsdKey,
    PsdLayer,
    PsdLayerFlag,
    PsdLayerMask,
    PsdLayers,
    PsdRectangle,
    PsdString,
    PsdUserMask,
    TiffImageSourceData,
    overlay,
)

import numpy as np

# read individual layer images from files
background: numpy.ndarray = imagecodecs.imread('background.png')
product: numpy.ndarray = imagecodecs.imread(r'C:\Users\cical\Desktop\Università\Progetto per Giuseppe\ComputerVision\pexels-arianna-jadé-4754648_mask.png')




# positions of layers in canvas
background_offset = (0, 0)
product_offset = (79, 83)

# create the ImageSourceData structure for the layered TIFF
image_source_data = TiffImageSourceData(
    name='Layered TIFF',
    psdformat=PsdFormat.LE32BIT,
    layers=PsdLayers(
        key=PsdKey.LAYER,
        has_transparency=False,
        layers=[
            PsdLayer(
                name='Background',
                rectangle=PsdRectangle(
                    background_offset[0],
                    background_offset[1],
                    background_offset[0] + background.shape[0],
                    background_offset[1] + background.shape[1],
                ),
                channels=[
                    PsdChannel(
                        channelid=PsdChannelId.CHANNEL0,
                        compression=PsdCompressionType.ZIP_PREDICTED,
                        data=background[..., 0],
                    ),
                    PsdChannel(
                        channelid=PsdChannelId.CHANNEL1,
                        compression=PsdCompressionType.ZIP_PREDICTED,
                        data=background[..., 1],
                    ),
                    PsdChannel(
                        channelid=PsdChannelId.CHANNEL2,
                        compression=PsdCompressionType.ZIP_PREDICTED,
                        data=background[..., 2],
                    ),
                ],
                mask=PsdLayerMask(),
                opacity=255,
                blendmode=PsdBlendMode.NORMAL,
                clipping=PsdClippingType.BASE,
                flags=PsdLayerFlag.PHOTOSHOP5 | PsdLayerFlag.TRANSPARENCY_PROTECTED,
                info=[
                    PsdString(PsdKey.UNICODE_LAYER_NAME, 'Background'),
                ],
            ),
            PsdLayer(
                name='Product',
                rectangle=PsdRectangle(
                    product_offset[0],
                    product_offset[1],
                    product_offset[0] + product.shape[0],
                    product_offset[1] + product.shape[1],
                ),
                channels=[
                    PsdChannel(
                        channelid=PsdChannelId.TRANSPARENCY_MASK,
                        compression=PsdCompressionType.ZIP_PREDICTED,
                        data=product[..., 3],
                    ),
                    PsdChannel(
                        channelid=PsdChannelId.CHANNEL0,
                        compression=PsdCompressionType.ZIP_PREDICTED,
                        data=product[..., 0],
                    ),
                    PsdChannel(
                        channelid=PsdChannelId.CHANNEL1,
                        compression=PsdCompressionType.ZIP_PREDICTED,
                        data=product[..., 1],
                    ),
                    PsdChannel(
                        channelid=PsdChannelId.CHANNEL2,
                        compression=PsdCompressionType.ZIP_PREDICTED,
                        data=product[..., 2],
                    ),
                ],
                mask=PsdLayerMask(),
                opacity=255,
                blendmode=PsdBlendMode.NORMAL,
                clipping=PsdClippingType.BASE,
                flags=PsdLayerFlag.PHOTOSHOP5,
                info=[
                    PsdString(PsdKey.UNICODE_LAYER_NAME, 'Product'),
                ],
            ),
        ],
    ),
    usermask=PsdUserMask(
        colorspace=PsdColorSpaceType.RGB,
        components=(65535, 0, 0, 0),
        opacity=50,
    ),
    info=[
        PsdEmpty(PsdKey.PATTERNS),
        PsdFilterMask(
            colorspace=PsdColorSpaceType.RGB,
            components=(65535, 0, 0, 0),
            opacity=50,
        ),
    ],
)


# create a composite of the layers
composite = overlay(
    (background, background_offset),
    (product, product_offset),
    shape=background.shape
)
# write a layered TIFF file
tifffile.imwrite(
    'layered.tif',
    # write composite as main TIFF image, accessible to regular TIFF readers
    composite,
    photometric='rgb',
    compression='adobe_deflate',
    # 72 dpi resolution
    resolution=((720000, 10000), (720000, 10000)),
    resolutionunit='inch',
    # do not write tifffile specific metadata
    metadata=None,
    # write layers and sRGB profile as extra tags
    extratags=[
        # ImageSourceData tag
        image_source_data.tifftag(),
        # InterColorProfile tag
        (34675, 7, None, imagecodecs.cms_profile('srgb'), True),
    ],
)

# read the ImageSourceData structure from the TIFF file
isd = TiffImageSourceData.fromtiff('layered.tif')
print(isd)
print(f'psdtags {__version__}')

# plot the layer and composite images in the TIFF file
for layer in isd.layers:
    tifffile.imshow(layer.asarray(), title=layer.name)
tifffile.imshow(tifffile.imread('layered.tif'), title='Composite', show=True)

But i recive error:

Traceback (most recent call last):
  File "C:\Users\cical\Desktop\Università\Progetto per Giuseppe\ComputerVision\prova.py", line 142, in <module>
    composite = overlay(
                ^^^^^^^^
  File "C:\Users\cical\anaconda3\Lib\site-packages\psdtags\psdtags.py", line 3775, in overlay
    over(composite, layer[0] / vmax, layer[1])
  File "C:\Users\cical\anaconda3\Lib\site-packages\psdtags\psdtags.py", line 3767, in over
    x = b[..., 3:] * (1.0 - a[..., 3:])
        ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
ValueError: operands could not be broadcast together with shapes (2738,1825,3,1) (2738,1825,0) 

Please, someone can help me? I'm a newbie

How can I edit psd file

Thanks for providing this tool, one question is how to edit the psd file, for example, layer creation and replacement

can output tif keep layers ?

some small tweak to the example on pypi page,
tif saved successful, but the layer were flattern.

Is is possible to keep the layers on ouput tif image ?

from tifffile import imread, imwrite
import psdtags

isd = psdtags.TiffImageResources.fromtiff('input_2layer.tif')
res = psdtags.TiffImageResources.fromtiff('input_2layer.tif')

image = imread('input_2layer.tif')
imwrite(
    'output_layered_tiff.tif',
    image,
    byteorder=isd.psdformat.byteorder,  # must match ImageSourceData
    photometric='rgb',  # must match ImageSourceData
    metadata=None,  # do not write any tifffile specific metadata
    extratags=[isd.tifftag(), res.tifftag()],
)

How to create an empty ImageResources

My tif file is generated from tifffile,
use
TiffImageResources.fromtiff('out.tif')
hint
TIFF file contains no ImageResources tag

How to create an empty ImageResources and assign it?

How to write a TIFF or PSD with Layer Mask ?

Fantastic work ! Thank you !

I'm trying to save a TIFF or PSD file with Layer Mask so i can edit it later if needed.

I would like to know how to do that if possible.

here are the files (original, mask)

original
mask

here how i am expecting to open the final file in photoshop :

photoshop

Appreciate your help !

All PsdBooleans are True

Hi there Christoph,

psdtags is great! But I just found a bug while doing an overly[*] paranoid assert(originalbytes == frombytes(originalbytes).tobytes() at the PsdLayer level). It seemed to me that a layer's infx value should have been 0000, but it was being written out as 1000. The same thing was true of knko. But I noticed that there wasn't an unexpected diff in the layer's clbl. They're all PsdBooleans. Then it occurred to me.. it's because they were all hardcoded by psdtags to 1000 (True), and only the clbl was originally true. Lo and behold:

In https://github.com/cgohlke/psdtags/blob/v2024.1.15/psdtags/psdtags.py#L2490 we see:

        value = bool(fh.read(1))

However:

% python3
>>> bool(b'\x01')
True
>>> bool(b'\x00')
True

I believe this is is making all PsdBoolean's read()/frombytes() True, regardless of input value. This also isn't the only place I see a bool(fh.read(1)), which I think is generally a bug for the above reason - unless the read literally returns False or None, you're always going to be True.

I tried patching this in my local checkout to check for fh.read(1) != '\x00', but it had really bizarre side effects - namely that write()/tobytes() output of my PsdLayer object ballooned in size. If I just hardcoded value = True I had identical broken output, but if I got it to parse correctly, I think some other routine is conditioning on some PsdBoolean somewhere, and since it's never been False before (or at least recently - I didn't check blame history) I'm guessing said codepath wasn't exercised.

Hope this helps,
-Andrew

[*] turns out maybe this wasn't overly paranoid, but "just right paranoid" ;)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.