663 lines
23 KiB
Python
663 lines
23 KiB
Python
from nodes import interrupt_processing
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
import torch
|
|
import numpy as np
|
|
import random
|
|
import math
|
|
from .text_to_image import BookToolsTextToImage
|
|
|
|
class AnyType(str):
|
|
"""A special class that is always equal in not equal comparisons. Credit to pythongosssss"""
|
|
|
|
def __ne__(self, __value: object) -> bool:
|
|
return False
|
|
|
|
any = AnyType("*")
|
|
loop_interation = 0
|
|
reset_global = False
|
|
class BookToolsPromptSelector:
|
|
"""
|
|
Selects and concatenates values from a dictionary based on input indices and ranges.
|
|
The input string contains 1-based indices which are converted to dictionary keys.
|
|
Assumes the dictionary values are strings which will be concatenated and returned as a single string.
|
|
"""
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
"""
|
|
Describes expected input types:
|
|
- 'dictionary': a dictionary where keys will be selected based on 'selected_indexes'.
|
|
- 'selected_indexes': a int listing keys by index, separated by commas, with optional ranges.
|
|
"""
|
|
return {
|
|
"required": {
|
|
"dictionary": ("DICTIONARY", ),
|
|
"selected_indexes": ("INT", {
|
|
"multiline": False,
|
|
"default": "1,2,3"
|
|
}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING", )
|
|
FUNCTION = "main"
|
|
CATEGORY = "selector"
|
|
OUTPUT_NODE = False
|
|
|
|
def main(self, dictionary: dict, selected_indexes: int):
|
|
if not isinstance(dictionary, dict):
|
|
raise ValueError("Conditioning must be a dictionary.")
|
|
|
|
if not isinstance(selected_indexes, int):
|
|
raise ValueError("selected_indexes must be an integer.")
|
|
|
|
result_string = str(dictionary.get(str(selected_indexes), ''))
|
|
|
|
return (result_string,)
|
|
|
|
class BookToolsPromptSchedule:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {"required": {"text": ("STRING", {"multiline": True, "default":"\"first page description\",\n\"second page description\""}),
|
|
"before_text": ("STRING", {"multiline": True,}),
|
|
"after_text": ("STRING", {"multiline": True,}),
|
|
}}
|
|
|
|
RETURN_TYPES = ("DICTIONARY",)
|
|
FUNCTION = "main"
|
|
CATEGORY = "prompt"
|
|
OUTPUT_NODE = False
|
|
|
|
def main(self, text, before_text, after_text):
|
|
"""
|
|
Splits the text on commas, applies before and after text to each segment,
|
|
and returns a dictionary with each formatted text indexed.
|
|
|
|
Parameters:
|
|
- text (str): Comma-separated text entries to be formatted.
|
|
- before_text (str): Text to prepend to each entry.
|
|
- after_text (str): Text to append to each entry.
|
|
|
|
Returns:
|
|
- dict: Dictionary with indexed formatted texts.
|
|
"""
|
|
segments = text.split('",')
|
|
formatted_texts = {}
|
|
|
|
for index, segment in enumerate(segments, start=1):
|
|
cleaned_segment = segment.replace('"', '').strip()
|
|
parts = [part for part in [before_text.strip(), cleaned_segment, after_text.strip()] if part]
|
|
formatted_text = ', '.join(parts)
|
|
formatted_texts[str(index)] = formatted_text
|
|
return (formatted_texts,)
|
|
|
|
class BookToolsLoop:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {"required": {"reset": ("BOOLEAN", {"default": False}),}}
|
|
|
|
RETURN_TYPES = ("LOOP", "INT",)
|
|
FUNCTION = "run"
|
|
CATEGORY = "loopback"
|
|
RETURN_NAMES = ("LOOP", "Iteration",)
|
|
|
|
def run(self, reset):
|
|
global loop_interation, reset_global
|
|
if (reset == True):
|
|
loop_interation = 1
|
|
reset_global = True
|
|
else:
|
|
reset_global = False
|
|
return (self, loop_interation)
|
|
|
|
@classmethod
|
|
def IS_CHANGED(self, reset):
|
|
global loop_interation, reset_global
|
|
loop_interation +=1
|
|
if (reset == True):
|
|
loop_interation = 1
|
|
reset_global = True
|
|
else:
|
|
reset_global = False
|
|
return loop_interation
|
|
|
|
class BookToolsLoopStart:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {"required": {
|
|
"first_loop": (any,),
|
|
"loop": ("LOOP",)
|
|
}
|
|
}
|
|
|
|
FUNCTION = "run"
|
|
CATEGORY = "loopback"
|
|
RETURN_TYPES = (any,)
|
|
RETURN_NAMES = ("*",)
|
|
|
|
def run(self, first_loop, loop):
|
|
global reset_global
|
|
if reset_global == True:
|
|
return (first_loop,)
|
|
if hasattr(loop, 'next'):
|
|
return (loop.next,)
|
|
return (first_loop,)
|
|
|
|
@classmethod
|
|
def IS_CHANGED(self, first_loop, loop):
|
|
global reset_global
|
|
if reset_global == True:
|
|
return (first_loop,)
|
|
if hasattr(loop, 'next'):
|
|
return id(loop.next)
|
|
return float("NaN")
|
|
|
|
class BookToolsLoopEnd:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {"required": { "send_to_next_loop": (any, ), "loop": ("LOOP",) }}
|
|
FUNCTION = "run"
|
|
CATEGORY = "loopback"
|
|
RETURN_TYPES = ()
|
|
OUTPUT_NODE = True
|
|
|
|
def run(self, send_to_next_loop, loop):
|
|
loop.next = send_to_next_loop
|
|
return ()
|
|
|
|
class BookToolsEndQueue:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {"required": { "boolean": ("BOOLEAN", {"default": False}), }}
|
|
FUNCTION = "main"
|
|
CATEGORY = "logic"
|
|
RETURN_TYPES = ()
|
|
OUTPUT_NODE = True
|
|
|
|
def main(self, boolean):
|
|
if (boolean==True):
|
|
interrupt_processing()
|
|
return ()
|
|
|
|
class BookToolsDownloadFont:
|
|
|
|
SUPPORTED_FONTS = {
|
|
"Delius": "Delius:wght@400",
|
|
"DejaVuSans-Bold": "DejaVuSans-Bold",
|
|
"FreeMono": "FreeMono:wght@400",
|
|
"Roboto": "RobotoMono-VariableFont_wght",
|
|
"Baloo 2": "Baloo2-VariableFont_wght",
|
|
"LuckiestGuy": "LuckiestGuy",
|
|
}
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"font_name": (list(cls.SUPPORTED_FONTS.keys()), {"default": "Open Sans"}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
FUNCTION = "download_font"
|
|
CATEGORY = "image/text"
|
|
|
|
def download_font(self, font_name):
|
|
import requests
|
|
import os
|
|
from urllib.parse import urlparse
|
|
|
|
fallback_font = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
|
|
|
|
fonts_dir = os.path.join(os.path.dirname(__file__), "fonts")
|
|
os.makedirs(fonts_dir, exist_ok=True)
|
|
|
|
font_filename = self.SUPPORTED_FONTS[font_name] + ".ttf"
|
|
font_path = os.path.join(fonts_dir, font_filename)
|
|
|
|
if os.path.exists(font_path):
|
|
try:
|
|
ImageFont.truetype(font_path, 12)
|
|
return (font_path,)
|
|
except Exception:
|
|
print(f"Cached font {font_name} is corrupt, attempting redownload...")
|
|
os.remove(font_path)
|
|
|
|
api_url = f"https://fonts.googleapis.com/css2?family={self.SUPPORTED_FONTS[font_name]}"
|
|
headers = {
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
|
}
|
|
|
|
try:
|
|
|
|
response = requests.get(api_url, headers=headers)
|
|
response.raise_for_status()
|
|
css = response.text
|
|
|
|
for line in css.split('\n'):
|
|
if 'src:' in line and 'url(' in line:
|
|
start = line.find('url(') + 4
|
|
end = line.find(')', start)
|
|
font_url = line[start:end].strip()
|
|
break
|
|
else:
|
|
raise ValueError("Could not find font URL in CSS")
|
|
|
|
parsed_url = urlparse(font_url)
|
|
if not all([parsed_url.scheme, parsed_url.netloc]):
|
|
raise ValueError("Invalid font URL")
|
|
|
|
font_response = requests.get(font_url, headers=headers)
|
|
font_response.raise_for_status()
|
|
|
|
with open(font_path, 'wb') as f:
|
|
f.write(font_response.content)
|
|
|
|
try:
|
|
ImageFont.truetype(font_path, 12)
|
|
return (font_path,)
|
|
except Exception as e:
|
|
print(f"Downloaded font {font_name} is invalid: {str(e)}")
|
|
if os.path.exists(font_path):
|
|
os.remove(font_path)
|
|
raise
|
|
|
|
except Exception as e:
|
|
print(f"Error downloading font {font_name}: {str(e)}")
|
|
print(f"Falling back to default font: {fallback_font}")
|
|
return (fallback_font,)
|
|
|
|
class BookToolsImageTextOverlay:
|
|
def __init__(self, device="cpu"):
|
|
self.device = device
|
|
_alignments = ["left", "right", "center"]
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"image": ("IMAGE",),
|
|
"text": ("STRING", {"multiline": True, "default": "Hello"}),
|
|
"textbox_width": ("INT", {"default": 200, "min": 1}),
|
|
"textbox_height": ("INT", {"default": 200, "min": 1}),
|
|
"max_font_size": ("INT", {"default": 80, "min": 1, "max": 256, "step": 1}),
|
|
"min_font_size": ("INT", {"default": 12, "min": 1, "max": 256, "step": 1}),
|
|
"font": ("STRING", {"default": "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"}),
|
|
"alignment": (cls._alignments, {"default": "center"}),
|
|
"color": ("STRING", {"default": "#000000"}),
|
|
"start_x": ("INT", {"default": 0}),
|
|
"start_y": ("INT", {"default": 0}),
|
|
"padding": ("INT", {"default": 50}),
|
|
"line_height_factor": ("FLOAT", {"default": 1.2, "min": 0.5, "max": 3.0, "step": 0.1}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("IMAGE",)
|
|
FUNCTION = "add_text_overlay"
|
|
CATEGORY = "image/text"
|
|
|
|
def calculate_text_width(self, text, font):
|
|
"""Calculate exact text width including kerning if available"""
|
|
try:
|
|
|
|
from PIL import ImageDraw
|
|
dummy_image = Image.new('RGB', (1, 1))
|
|
draw = ImageDraw.Draw(dummy_image)
|
|
bbox = draw.textbbox((0, 0), text, font=font)
|
|
return bbox[2] - bbox[0]
|
|
except:
|
|
|
|
return sum(font.getbbox(char)[2] - font.getbbox(char)[0] for char in text)
|
|
|
|
def calculate_text_size(self, text, font_size, font_path, max_width, max_height, line_height_factor):
|
|
try:
|
|
font = ImageFont.truetype(font_path, font_size)
|
|
except Exception as e:
|
|
print(f"Font error: {str(e)}, using default")
|
|
font = ImageFont.load_default()
|
|
|
|
paragraphs = text.split('\n')
|
|
lines = []
|
|
max_line_width = 0
|
|
|
|
for paragraph in paragraphs:
|
|
|
|
words = paragraph.split()
|
|
if not words:
|
|
lines.append("")
|
|
continue
|
|
|
|
current_line = []
|
|
current_width = 0
|
|
|
|
for word in words:
|
|
word_bbox = font.getbbox(word)
|
|
word_width = word_bbox[2] - word_bbox[0]
|
|
|
|
space_width = font.getbbox(' ')[2] if current_line else 0
|
|
|
|
if current_width + word_width + space_width <= max_width:
|
|
if current_line:
|
|
current_width += space_width
|
|
current_line.append(word)
|
|
current_width += word_width
|
|
else:
|
|
if current_line:
|
|
lines.append(' '.join(current_line))
|
|
max_line_width = max(max_line_width, current_width)
|
|
current_line = [word]
|
|
current_width = word_width
|
|
|
|
if current_line:
|
|
lines.append(' '.join(current_line))
|
|
max_line_width = max(max_line_width, current_width)
|
|
|
|
line_height = int(font_size * line_height_factor)
|
|
total_height = len(lines) * line_height
|
|
|
|
fits = (max_line_width <= max_width) and (total_height <= max_height)
|
|
|
|
return lines, fits, max_line_width, total_height
|
|
|
|
def add_text_overlay(self, image, text, textbox_width, textbox_height, max_font_size, min_font_size,
|
|
font, alignment, color, start_x, start_y, padding, line_height_factor):
|
|
image_tensor = image
|
|
image_np = image_tensor.cpu().numpy()
|
|
image_pil = Image.fromarray((image_np.squeeze(0) * 255).astype(np.uint8))
|
|
color_rgb = tuple(int(color.lstrip("#")[i:i+2], 16) for i in (0, 2, 4))
|
|
|
|
effective_width = textbox_width - 2 * padding
|
|
effective_height = textbox_height - 2 * padding
|
|
|
|
low, high = min_font_size, max_font_size
|
|
optimal_font_size = min_font_size
|
|
optimal_lines = []
|
|
|
|
while low <= high:
|
|
mid = (low + high) // 2
|
|
lines, fits, _, _ = self.calculate_text_size(text, mid, font, effective_width, effective_height, line_height_factor)
|
|
|
|
if fits:
|
|
optimal_font_size = mid
|
|
optimal_lines = lines
|
|
low = mid + 1
|
|
else:
|
|
high = mid - 1
|
|
|
|
if not optimal_lines:
|
|
optimal_font_size = min_font_size
|
|
optimal_lines, _, _, _ = self.calculate_text_size(text, min_font_size, font, effective_width, effective_height, line_height_factor)
|
|
|
|
loaded_font = ImageFont.truetype(font, optimal_font_size)
|
|
draw = ImageDraw.Draw(image_pil)
|
|
|
|
line_height = int(optimal_font_size * line_height_factor)
|
|
total_text_height = len(optimal_lines) * line_height
|
|
y = start_y + padding
|
|
|
|
if total_text_height <= effective_height:
|
|
y += (effective_height - total_text_height) // 2
|
|
|
|
for line in optimal_lines:
|
|
if y + line_height > start_y + textbox_height:
|
|
break
|
|
|
|
line_width = self.calculate_text_width(line, loaded_font)
|
|
|
|
if alignment == "left":
|
|
x = start_x + padding
|
|
elif alignment == "right":
|
|
x = start_x + effective_width - line_width + padding
|
|
else:
|
|
|
|
x = start_x + (effective_width - line_width) // 2 + padding
|
|
|
|
draw.text((x, y), line, fill=color_rgb, font=loaded_font)
|
|
y += line_height
|
|
|
|
image_tensor_out = torch.tensor(np.array(image_pil).astype(np.float32) / 255.0)
|
|
image_tensor_out = torch.unsqueeze(image_tensor_out, 0)
|
|
return (image_tensor_out,)
|
|
|
|
class BookToolsCalculateTextGrowth:
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"text": ("STRING", {"multiline": True, "default": "Hello"}),
|
|
"mask_width": ("INT", {"default": 200, "min": 1}),
|
|
"mask_height": ("INT", {"default": 200, "min": 1}),
|
|
"min_font_size": ("INT", {"default": 12, "min": 1, "max": 256, "step": 1}),
|
|
"font": ("STRING", {"default": "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"}),
|
|
"padding": ("INT", {"default": 50}),
|
|
"line_height_factor": ("FLOAT", {"default": 1.2, "min": 0.5, "max": 3.0, "step": 0.1}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("INT",)
|
|
RETURN_NAMES = ("growth_percentage",)
|
|
FUNCTION = "calculate"
|
|
CATEGORY = "image/text"
|
|
|
|
def calculate_text_bounds(self, text, font_size, font_path, max_width, line_height_factor):
|
|
font = ImageFont.truetype(font_path, font_size)
|
|
paragraphs = text.split('\n')
|
|
max_line_width = 0
|
|
total_lines = 0
|
|
|
|
for paragraph in paragraphs:
|
|
words = paragraph.split()
|
|
current_line = []
|
|
current_width = 0
|
|
|
|
for word in words:
|
|
word_bbox = font.getbbox(word)
|
|
word_width = word_bbox[2] - word_bbox[0]
|
|
space_width = font.getbbox(' ')[2] if current_line else 0
|
|
|
|
if current_width + word_width + space_width <= max_width:
|
|
current_line.append(word)
|
|
current_width += word_width + space_width
|
|
else:
|
|
max_line_width = max(max_line_width, current_width)
|
|
current_line = [word]
|
|
current_width = word_width
|
|
total_lines += 1
|
|
|
|
if current_line:
|
|
max_line_width = max(max_line_width, current_width)
|
|
total_lines += 1
|
|
|
|
line_height = int(font_size * line_height_factor)
|
|
total_height = total_lines * line_height
|
|
|
|
return max_line_width, total_height
|
|
|
|
def calculate(self, text, mask_width, mask_height, min_font_size, font, padding, line_height_factor):
|
|
textbox_width = mask_width + (2 * padding)
|
|
textbox_height = mask_height + (2 * padding)
|
|
|
|
effective_width = textbox_width - 2 * padding
|
|
effective_height = textbox_height - 2 * padding
|
|
|
|
text_width, text_height = self.calculate_text_bounds(
|
|
text, min_font_size, font, effective_width, line_height_factor
|
|
)
|
|
|
|
width_ratio = (text_width / mask_width) / 2
|
|
height_ratio = (text_height / mask_height) / 2
|
|
|
|
growth_ratio = max(width_ratio, height_ratio)
|
|
|
|
if growth_ratio <= 0.5:
|
|
return (0,)
|
|
|
|
growth_percentage = min(100, int((growth_ratio - 0.5) * 200))
|
|
|
|
return (growth_percentage,)
|
|
|
|
class BookToolsRandomTextOverlay:
|
|
def __init__(self, device="cpu"):
|
|
self.device = device
|
|
_alignments = ["left", "center"]
|
|
_positions = ["top", "bottom"]
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"image": ("IMAGE",),
|
|
"text": ("STRING", {"multiline": True, "default": "Hello"}),
|
|
"font_size": ("INT", {"default": 30, "min": 8, "max": 100, "step": 1}),
|
|
"font": ("STRING", {"default": "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"}),
|
|
"color": ("STRING", {"default": "#000000"}),
|
|
"padding": ("INT", {"default": 20, "min": 0}),
|
|
"margin": ("INT", {"default": 20, "min": 0}),
|
|
"corner_radius": ("INT", {"default": 15, "min": 0}),
|
|
"line_height_factor": ("FLOAT", {"default": 1.2, "min": 0.5, "max": 3.0, "step": 0.1}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("IMAGE",)
|
|
FUNCTION = "add_random_text_overlay"
|
|
CATEGORY = "image/text"
|
|
|
|
def add_random_text_overlay(self, image, text, font_size, font, color, padding, margin, corner_radius, line_height_factor):
|
|
image_tensor = image
|
|
image_np = image_tensor.cpu().numpy()
|
|
image_pil = Image.fromarray((image_np.squeeze(0) * 255).astype(np.uint8))
|
|
|
|
image_pil = image_pil.convert('RGBA')
|
|
color_rgb = tuple(int(color.lstrip("#")[i:i+2], 16) for i in (0, 2, 4))
|
|
|
|
bg_overlay = Image.new('RGBA', image_pil.size, (0, 0, 0, 0))
|
|
bg_draw = ImageDraw.Draw(bg_overlay)
|
|
|
|
text_overlay = Image.new('RGBA', image_pil.size, (0, 0, 0, 0))
|
|
text_draw = ImageDraw.Draw(text_overlay)
|
|
|
|
loaded_font = ImageFont.truetype(font, font_size)
|
|
|
|
max_width = image_pil.width - (margin * 2) - (padding * 2)
|
|
line_height = int(font_size * line_height_factor)
|
|
space_width = text_draw.textlength(" ", font=loaded_font)
|
|
|
|
lines = []
|
|
current_line = []
|
|
current_width = 0
|
|
|
|
for word in text.split():
|
|
word_width = text_draw.textlength(word, font=loaded_font)
|
|
|
|
if current_width + word_width <= max_width:
|
|
current_line.append(word)
|
|
current_width += word_width + space_width
|
|
else:
|
|
if current_line:
|
|
lines.append(" ".join(current_line))
|
|
|
|
if word_width > max_width:
|
|
chars = []
|
|
char_width = 0
|
|
for c in word:
|
|
cw = text_draw.textlength(c, font=loaded_font)
|
|
if char_width + cw > max_width:
|
|
lines.append("".join(chars))
|
|
chars = []
|
|
char_width = 0
|
|
chars.append(c)
|
|
char_width += cw
|
|
if chars:
|
|
lines.append("".join(chars))
|
|
else:
|
|
current_line = [word]
|
|
current_width = word_width + space_width
|
|
|
|
if current_line:
|
|
lines.append(" ".join(current_line))
|
|
|
|
max_line_width = max(text_draw.textlength(line, font=loaded_font) for line in lines)
|
|
total_height = len(lines) * line_height
|
|
|
|
bg_width = int(max_line_width + padding * 2)
|
|
bg_height = int(total_height + padding * 2)
|
|
|
|
position = random.choice(self._positions)
|
|
alignment = random.choice(self._alignments)
|
|
|
|
if position == "top":
|
|
bg_y = margin
|
|
opacity = 0.9
|
|
else:
|
|
bg_y = image_pil.height - bg_height - margin
|
|
opacity = 0.8
|
|
|
|
if alignment == "left":
|
|
bg_x = margin
|
|
else:
|
|
bg_x = (image_pil.width - bg_width) // 2
|
|
|
|
alpha = int(255 * opacity)
|
|
bg_color = (255, 255, 255, alpha)
|
|
bg_draw.rounded_rectangle(
|
|
[bg_x, bg_y, bg_x + bg_width, bg_y + bg_height],
|
|
radius=corner_radius,
|
|
fill=bg_color
|
|
)
|
|
|
|
y = bg_y + padding
|
|
for line in lines:
|
|
if y + line_height > bg_y + bg_height:
|
|
break
|
|
|
|
line_width = text_draw.textlength(line, font=loaded_font)
|
|
|
|
if alignment == "left":
|
|
x = bg_x + padding
|
|
else:
|
|
x = bg_x + (bg_width - line_width) // 2
|
|
|
|
text_draw.text((x+1, y+1), line, fill=(255,255,255,150), font=loaded_font)
|
|
|
|
text_draw.text((x, y), line, fill=(*color_rgb, 255), font=loaded_font)
|
|
y += line_height
|
|
|
|
result = Image.alpha_composite(image_pil, bg_overlay)
|
|
result = Image.alpha_composite(result, text_overlay)
|
|
|
|
result = result.convert('RGB')
|
|
|
|
image_tensor_out = torch.tensor(np.array(result).astype(np.float32) / 255.0)
|
|
image_tensor_out = torch.unsqueeze(image_tensor_out, 0)
|
|
return (image_tensor_out,)
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"BTPromptSelector": BookToolsPromptSelector,
|
|
"BTPromptSchedule": BookToolsPromptSchedule,
|
|
"BTLoop": BookToolsLoop,
|
|
"BTLoopStart": BookToolsLoopStart,
|
|
"BTLoopEnd": BookToolsLoopEnd,
|
|
"BTEndQueue": BookToolsEndQueue,
|
|
"BTImageTextOverlay": BookToolsImageTextOverlay,
|
|
"BTDownloadFont": BookToolsDownloadFont,
|
|
"BTTextGrowth": BookToolsCalculateTextGrowth,
|
|
"BTRandomTextOverlay": BookToolsRandomTextOverlay,
|
|
"BTTextToImage": BookToolsTextToImage,
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"BTPromptSelector": "[Book Tools] Prompt Selector",
|
|
"BTPromptSchedule": "[Book Tools] Prompt Batch Schedule",
|
|
"BTLoop": "[Book Tools] Loop",
|
|
"BTLoopStart": "[Book Tools] Loop Start",
|
|
"BTLoopEnd": "[Book Tools] Loop End",
|
|
"BTEndQueue": "[Book Tools] End Queue",
|
|
"BTImageTextOverlay": "[Book Tools] Image Text Overlay",
|
|
"BTDownloadFont": "[Book Tools] Download Font",
|
|
"BTTextGrowth": "[Book Tools] Calculate Text Growth",
|
|
"BTRandomTextOverlay": "[Book Tools] Random Text Overlay",
|
|
"BTTextToImage": "[Book Tools] Text to Image",
|
|
} |