Skip to content

Instantly share code, notes, and snippets.

@AdamStormhardtGH
Created January 5, 2024 02:33
Show Gist options
  • Save AdamStormhardtGH/6739cf548435ea64c012196ef0ed9479 to your computer and use it in GitHub Desktop.
Save AdamStormhardtGH/6739cf548435ea64c012196ef0ed9479 to your computer and use it in GitHub Desktop.
Using python to write a new layer to a psd file
"""
Exmample of how to read a PSD in python, add a layer and write image data to it.
With this you can automate a bunch of PSD functionality
"""
import pytoshop
from pytoshop import layers
from pytoshop.enums import BlendMode
import numpy as np
import cv2
# This is creative commons. Use it and be cool to one another.
# psd-tools needs this functionality and if you have time I'm sure a PR would be highly appreciated
# urls for images
my_image_url = "art/my_cool_image.png"
def create_rgba_data_for_image(image_url: str) -> np.ndarray:
"""
Reads in image and converts it to RGBA
:param image_url: The url of the image to be converted eg. art/my_image.png
:return: The image as a numpy array
"""
# do initial read
image = cv2.imread(image_url, 1) # reads using opencv and as rgb format
# Convert the image to RGBA
rgba_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGBA) # todo: see if we can do this in one step
return rgba_image # Return the numpy array directly
def insert_psd_layer(psd, image_data, layer_name="a new photoshop layer appears!", blending_mode=BlendMode.normal):
"""
Inserts a layer into a PSD
Expects a PSD to exist, but there are other methods which will create a PSD from scratch
:param psd: The PSD to insert the layer into. This is done using `psd = pytoshop.read(fd)` after you open the image as a read binary
:param image_data: The image data as a numpy array. Use the function `create_rgba_data_for_image` to convert an image to RGBA
:param layer_name: The name of the layer to be inserted
:param blending_mode: The blending mode of the layer to be inserted. Use `BlendMode.normal` for normal blending
"""
num_channels = image_data.shape[2]
channel_data = [layers.ChannelImageData(image=image_data[:, :, i], compression=1) for i in range(num_channels)]
layer_channels = {
-1: channel_data[3] if num_channels > 3 else None, # if no alpha channel, set to none
0: channel_data[0],
1: channel_data[1],
2: channel_data[2],
}
layer_channels = {k: v for k, v in layer_channels.items() if v is not None}
layer_record = layers.LayerRecord(
channels=layer_channels,
top=0,
bottom=image_data.shape[0],
left=0,
right=image_data.shape[1],
blend_mode=blending_mode,
name=layer_name,
opacity=255,
)
psd.layer_and_mask_info.layer_info.layer_records.append(layer_record)
return psd
##########################################################
# EXAMPLE TO READ AND WRITE A PSD WITH A FANCY NEW LAYER #
##########################################################
with open("example2.psd", "rb") as fd:
psd = pytoshop.read(fd)
# Get the RGBA image data
rgba_image_data = create_rgba_data_for_image(my_image_url)
# Insert the layer
insert_psd_layer(psd, rgba_image_data, "test layer name", BlendMode.normal) # Use string 'norm' for blending_mode
# Write the modified PSD to a new file
# Writing to the same open file is weird and can probably be done, but I can't be bothered dealing with it atm
with open("example1.psd", "wb") as fd2:
psd.write(fd2)
@AdamStormhardtGH
Copy link
Author

<3 to AuthorSultra for the example in his repo
https://github.com/AuthorSultra/Layer-PSD/blob/main/scripts/layer_divider_modules/mask_utils.py#L5
And of course to psd-tools and pytoshop for building packages

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment