commit LoadPSD node
This commit is contained in:
+26
-2
@@ -1246,11 +1246,20 @@ def decode_watermark(image:Image, watermark_image_size:int=94) -> Image:
|
||||
except Exception as e:
|
||||
log(f"blind watermark extract fail, {e}")
|
||||
ret_image = Image.new("RGB", (64, 64), color="black")
|
||||
|
||||
ret_image = normalize_gray(ret_image)
|
||||
|
||||
return ret_image
|
||||
|
||||
def generate_text_image(width:int, height:int, text:str, font_file:str, text_scale:float=1, font_color:str="#FFFFFF",) -> Image:
|
||||
image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(image)
|
||||
font_size = int(width / len(text) * text_scale)
|
||||
font = ImageFont.truetype(font_file, font_size)
|
||||
bbox = draw.textbbox((0, 0), text, font=font)
|
||||
text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
||||
x = int((width - text_width) / 2)
|
||||
y = int((height - text_height) / 2) - int(font_size / 2)
|
||||
draw.text((x, y), text, font=font, fill=font_color)
|
||||
return image
|
||||
|
||||
'''Mask Functions'''
|
||||
@lru_cache(maxsize=1, typed=False)
|
||||
@@ -1616,6 +1625,21 @@ def Hex_to_HSV_255level(inhex:str) -> list:
|
||||
HSV = colorsys.rgb_to_hsv(RGB[0] / 255.0, RGB[1] / 255.0, RGB[2] / 255.0)
|
||||
return [int(x * 255) for x in HSV]
|
||||
|
||||
|
||||
def HSV_255level_to_Hex(HSV: list) -> str:
|
||||
if len(HSV) != 3 or any((not isinstance(v, int) or v < 0 or v > 255) for v in HSV):
|
||||
raise ValueError('Invalid HSV values, each value should be an integer between 0 and 255')
|
||||
|
||||
H, S, V = HSV
|
||||
RGB = tuple(int(x * 255) for x in hsv_to_rgb(H / 255.0, S / 255.0, V / 255.0))
|
||||
|
||||
# Convert RGB values to hexadecimal format
|
||||
hex_r = format(RGB[0], '02x')
|
||||
hex_g = format(RGB[1], '02x')
|
||||
hex_b = format(RGB[2], '02x')
|
||||
|
||||
return '#' + hex_r + hex_g + hex_b
|
||||
|
||||
'''Value Functions'''
|
||||
|
||||
def step_value(start_value, end_value, total_step, step) -> float: # 按当前步数在总步数中的位置返回比例值
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
import os
|
||||
import numpy as np
|
||||
import torch
|
||||
import folder_paths
|
||||
import node_helpers
|
||||
from nodes import LoadImage
|
||||
from PIL import Image,ImageOps,ImageSequence,ImageDraw,ImageFont
|
||||
from psd_tools import PSDImage
|
||||
from psd_tools.api.layers import Layer
|
||||
from .imagefunc import pil2tensor, log, FONT_DICT, generate_text_image
|
||||
|
||||
|
||||
class LoadPSD(LoadImage):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
files = [f for f in os.listdir(input_dir) if os.path.isfile(
|
||||
os.path.join(input_dir, f)) and f.endswith(".psd")]
|
||||
fine_layer_method = ["layer_index", "layer_name"]
|
||||
return {"required":{
|
||||
"image": (sorted(files), {"image_upload": True}),
|
||||
"file_path": ("STRING", {"default": ""}),
|
||||
"include_hidden_layer": ("BOOLEAN", {"default": False}),
|
||||
"find_layer_by": (fine_layer_method,),
|
||||
"layer_index": ("INT", {"default": 0, "min": -1, "max": 999, "step": 1}),
|
||||
"layer_name": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE",)
|
||||
RETURN_NAMES = ("flat_image", "layer_image", "all_layers",)
|
||||
FUNCTION = "load_psd"
|
||||
CATEGORY = '😺dzNodes/LayerUtility'
|
||||
|
||||
def load_psd(self, image, file_path, include_hidden_layer, layer_index, find_layer_by, layer_name,):
|
||||
|
||||
NODE_NAME = 'LoadPSD'
|
||||
number_of_layers = 1
|
||||
layer_image = []
|
||||
all_layers = []
|
||||
if file_path == "":
|
||||
psd_file_path = folder_paths.get_annotated_filepath(image)
|
||||
else:
|
||||
psd_file_path = folder_paths.get_annotated_filepath(file_path)
|
||||
flat_image = Image.open(psd_file_path).convert("RGB")
|
||||
width, height = flat_image.size
|
||||
if image.endswith(".psd"):
|
||||
log(f"{NODE_NAME} -> Loading PSD file: {psd_file_path}")
|
||||
psd_image = PSDImage.open(psd_file_path)
|
||||
layers = []
|
||||
for layer in psd_image:
|
||||
if include_hidden_layer:
|
||||
if not layer.is_visible():
|
||||
layer.visible = True
|
||||
layers.append(layer)
|
||||
else:
|
||||
if layer.is_visible():
|
||||
layers.append(layer)
|
||||
|
||||
number_of_layers = len(layers)
|
||||
for i in range(number_of_layers):
|
||||
layer_canvas = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
||||
layer_canvas.paste(layers[i].composite(), layers[i].bbox)
|
||||
all_layers.append(pil2tensor(layer_canvas))
|
||||
if find_layer_by == "layer_name":
|
||||
if layers[i].name == layer_name:
|
||||
layer_image.append(pil2tensor(layer_canvas))
|
||||
log(f"{NODE_NAME} -> Layer {i} : {layer.name} found.")
|
||||
elif find_layer_by == "layer_index":
|
||||
if i == layer_index:
|
||||
layer_image.append(pil2tensor(layer_canvas))
|
||||
log(f"{NODE_NAME} -> Layer {i} : {layer.name} found.")
|
||||
|
||||
text = "Layer Not Found!"
|
||||
font_file = list(FONT_DICT.values())[0]
|
||||
empty_layer_image = generate_text_image(flat_image.width, flat_image.height, text, font_file, font_color="#F01000")
|
||||
if layer_image == []:
|
||||
if layer_index == -1:
|
||||
layer_image.append(all_layers[-1])
|
||||
elif find_layer_by == "layer_name":
|
||||
log(f'{NODE_NAME} -> Layer "{layer_name}" not found, top layer will be output.', message_type="warning")
|
||||
elif find_layer_by == "layer_index":
|
||||
log(f'{NODE_NAME} -> Layer index {layer_index} not found, top layer will be output.', message_type="warning")
|
||||
layer_image.append(pil2tensor(empty_layer_image))
|
||||
else:
|
||||
layer_image.append(pil2tensor(flat_image))
|
||||
all_layers.append(pil2tensor(flat_image))
|
||||
|
||||
return (torch.cat([pil2tensor(flat_image),], dim=0), torch.cat(layer_image, dim=0), torch.cat(all_layers, dim=0),)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: LoadPSD": LoadPSD,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: LoadPSD": "LayerUtility: Load PSD",
|
||||
}
|
||||
+14
-7
@@ -1,4 +1,5 @@
|
||||
import os.path
|
||||
import shutil
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import datetime
|
||||
from .imagefunc import *
|
||||
@@ -24,7 +25,7 @@ class SaveImagePlus:
|
||||
"meta_data": ("BOOLEAN", {"default": False}),
|
||||
"blind_watermark": ("STRING", {"default": ""}),
|
||||
"save_workflow_as_json": ("BOOLEAN", {"default": False}),
|
||||
"preview": ("BOOLEAN", {"default": False}),
|
||||
"preview": ("BOOLEAN", {"default": True}),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||
}
|
||||
@@ -94,10 +95,15 @@ class SaveImagePlus:
|
||||
|
||||
|
||||
preview_filename = ""
|
||||
if not os.path.exists(custom_path):
|
||||
if custom_path != "":
|
||||
raise FileNotFoundError("custom_path is not a valid path")
|
||||
else:
|
||||
if custom_path != "":
|
||||
if not os.path.exists(custom_path):
|
||||
try:
|
||||
os.makedirs(custom_path)
|
||||
except Exception as e:
|
||||
log(f"Error: {NODE_NAME} skipped, because unable to create temporary folder.",
|
||||
message_type='warning')
|
||||
raise FileNotFoundError(f"cannot create custom_path {custom_path}, {e}")
|
||||
|
||||
full_output_folder = os.path.normpath(custom_path)
|
||||
# save preview image to temp_dir
|
||||
if os.path.isdir(temp_dir):
|
||||
@@ -106,7 +112,8 @@ class SaveImagePlus:
|
||||
os.makedirs(temp_dir)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
log(f"Error: {NODE_NAME} skipped, because unable to create temporary folder.", message_type='warning')
|
||||
log(f"Error: {NODE_NAME} skipped, because unable to create temporary folder.",
|
||||
message_type='warning')
|
||||
try:
|
||||
preview_filename = os.path.join(generate_random_name('saveimage_preview_', '_temp', 16) + '.png')
|
||||
img.save(os.path.join(temp_dir, preview_filename))
|
||||
@@ -133,7 +140,7 @@ class SaveImagePlus:
|
||||
if img.mode == "RGBA":
|
||||
img = img.convert("RGB")
|
||||
img.save(image_file_name, quality=quality)
|
||||
log(f"Saving image to {image_file_name}")
|
||||
log(f"{NODE_NAME} -> Saving image to {image_file_name}")
|
||||
|
||||
if save_workflow_as_json:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user