mdboom/pytoshop

Can't add image data to layer

TravisEvashkevich opened this issue · 6 comments

  • pytoshop version: 1.2.0
  • Python version: 3.5.3
  • Operating System: Win10 64bit

Description

Trying to make a texture atlassing script using Pillow and pytoshop.
The idea would be that I can give the script a bunch of images, read them in with Pillow and then write them to a PSD with pytoshop.

I'm trying to achive
-Groups (named)
-Named layers in groups with image data.

What I Did

import os
import pip
try:
	print("Log: Trying to Import Pillow.")
	#it might be that we don't have Pillow installed on the system so we should check first
	from PIL import Image
	import numpy
	print ("Log: Imported successfully.")
except ImportError:
	print("Log: Pillow not installed, Installing via Pip")
	pip.main(['install',"Pillow"])
	from PIL import Image
	print("\n\n")

try:
	print("Log: Trying to Import pytoshop.")
	#it might be that we don't have Pillow installed on the system so we should check first
	import pytoshop
	from pytoshop.user import nested_layers
	from pytoshop import enums
	from pytoshop.image_data import ImageData
	print ("Log: Imported successfully.")
except ImportError:
	print("Log: Pytoshop not installed, Installing via Pip")
	pip.main(['install',"six"])
	pip.main(['install',"pytoshop"])
	import pytoshop
	from pytoshop.user import nested_layers
	print("\n\n")

cwd = os.getcwd()
tgas = [x for x in os.listdir(cwd) if x.lower().endswith(".tga")]

with open('AtlasBase-2048.psd', 'rb') as fd:
	psd = pytoshop.PsdFile.read(fd)
	layers = []
	for tga in tgas:
		#print(tga)
		im = Image.open(tga)
		data = ImageData(channels=numpy.array(im))
		newLayer = nested_layers.Image(name=tga.replace(".tga", ""), visible=True, opacity=255, group_id=0,
										blend_mode=enums.BlendMode.normal, top=0,
										left=0, bottom=im.height, right=im.width, channels=data.channels,
										metadata=None, layer_color=0, color_mode=None)
		layers.append(newLayer)

	output = nested_layers.nested_layers_to_psd(layers, color_mode=3, size=(1024, 1024))

	with open('updated.psd', 'wb') as fd:
		output.write(fd)
		psd.write(fd)

Getting an error of

ValueError: Channels in image have different shapes or do not match layer

have tried:
-just putting the pillow image into the channels
-feeding just specific parts of the image array to channels
-not setting bottom/right
-not setting size in nested_layers_to_psd call
If I don't put an image(data or array) in channels when making a layer it will complete and make the PSD with layers named correctly but not with the needed image data in it.

Current code is just trying to throw the image data into the layer but I'd also like to be able to "position" it as well (if I have 4 x 1024 square images, I'd prefer to be able to have them laid out in a square of the 4 images (top left, top right, bottom left, bottom right). Not sure if this would have to be done via pillow (position then pass data to pytoshop) or if is possible in pytoshop directly

Thanks for the report.

The main issue here is that the input image data is in the wrong shape. Here's the docs for nested_layers.Image.channels:

        The channel image data. May be one of the following:
        - A dictionary from `enums.ChannelId` to 2-D numpy arrays.
        - A 3-D numpy array of the shape (num_channels, height, width)
        - A list of numpy arrays where each is a channel.

The data that Pillow gives is (height, width, num_channels). You can convert as follows: (It's probably also possible to do using some Numpy striding magic, but it's just as convenient to make a list of channels here):

    im = Image.open(tga)
    arr = numpy.array(im)
    channels = [arr[:,:,0], arr[:,:,1], arr[:,:,2]]
    data = ImageData(channels=numpy.array(im))
    newLayer = nested_layers.Image(name=tga.replace(".tga", ""), visible=True, opacity=255, group_id=0,
                                   blend_mode=enums.BlendMode.normal, top=0,
                                   left=0, channels=channels,
                                   metadata=None, layer_color=0, color_mode=None)

This finishes for me, but I haven't tested the result -- I don't actually have a license to Photoshop right now, if you can believe it... :) (Adobe, are you listening? Want to gift a humble open source developer supporting your product? )

Also: leaving out bottom and right is probably most convenient here, so you don't have to do the math yourself. You should be able to set left and top to whatever you want in order to lay out the layers how you'd like.

Lastly, I see that you are reading in a psd file, and then writing it out after the new layers you've created. This probably creates an invalid PSD file (you are basically putting the literal contents of two PSD Files into one). Are you trying to add the layers to an existing file? In that case, you'll want to read the PSD, convert it to nested layers using nested_layers.psd_to_nested_layers, add your new layers to that structure, and then convert back using nested_layers.nested_layers_to_psd and then write that out.

Works, awesome.
Yeah I was trying to figure out how to create a new PSD from scratch with the dimensions and stuff I want (which I still haven't figured out) but other than that it's all working great now! Thanks. I might try to get a PR together for adding some more examples and such (as I had a few other experienced TA's looking at this and we were scratching our heads trying to get things going...)

Great! A PR would be very welcome.

is there a way to (or need to even) specify PSD size or does it just go by the biggest layers shape?
as in if I have 4 1024 sized images being put into the psd but I want to lay them out so it's a 2x2, do I have make a psd explicitly 2048 or will just doing the output.write(fd) line just determine that automatically?
Edit:
Just got it working without having to specify. Answered my own question. Thanks!

I never got around to a PR, but here is a gist of how I got some example and helpful functions to get things running.

what is the color mode in nested_layers.nested_layers_to_psd(layers, color_mode=3,) if we want to save RGBA in psd .if i used color mode as 3 it writting only RGB in psd .