workflow replacement

This commit is contained in:
Raykosan
2025-04-08 13:01:58 +03:00
parent 4f06d12e2f
commit a6b14cd765
11 changed files with 0 additions and 405 deletions
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2025 Raykosan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-96
View File
@@ -1,96 +0,0 @@
## 🦊 ComfyUI_RaykoStudio
ComfyUI_RaykoStudio — это набор кастомных нод для ComfyUI, предоставляющий дополнительные возможности для работы с изображениями
![Demo](web/preview.png)
## 🦊 RS_RusTextOverlay Node
## 🔥 Описание
`RS_RusTextOverlay` позволяет накладывать текст на изображение в области, заданной маской. Нода поддерживает поворот текста в соответствии с углом наклона маски, автоматический подбор размера шрифта, кастомные шрифты, перенос строк и настройку прозрачности текста.
### Возможности
- Наложение текста на изображение в области, заданной маской.
- Автоматический поворот текста в соответствии с углом наклона маски (опционально).
- Поддержка кастомных шрифтов (`.ttf` или `.otf`), загружаемых из папки `fonts`.
- Автоматический подбор размера шрифта для вписывания текста в область маски.
- Настройка цвета текста и прозрачности (от 0 до 100%).
- Настройка выравнивания текста (вертикальное: top/center/bottom, горизонтальное: left/center/right).
- Настройка отступов (padding) вокруг текста.
### Входы
- `image`: Изображение (тензор формата RGB).
- `mask`: Маска (тензор формата L), определяющая область для текста.
### Выходы
- `IMAGE`: Изображение с наложенным текстом (тензор формата RGB).
## ⚙️ Параметры
- `text`: Текст для наложения. Поддерживает перенос строк.
- `font_name`: Выберите шрифт из списка доступных в папке `fonts` (или используйте `default`).
- `text_color`: Цвет текста (в формате HEX, например `#FFFFFF`).
- `text_opacity`: Прозрачность текста (0–100%, где 0 — полностью прозрачный, 100 — полностью непрозрачный).
- `min_font_size`: Минимальный размер шрифта (если текст не помещается, используется шрифт по умолчанию).
- `padding`: Отступы вокруг текста внутри маски.
- `vertical_align`: Вертикальное выравнивание текста (top/center/bottom).
- `horizontal_align`: Горизонтальное выравнивание текста (left/center/right).
- `rotate_with_mask`: Включить/выключить поворот текста в соответствии с углом наклона маски.
## 🛠 Установка
1. Клонируйте репозиторий в папку `ComfyUI/custom_nodes/`:
git clone [https://github.com/Raykosan/ComfyUI_RaykoStudio.git](https://github.com/Raykosan/ComfyUI_RaykoStudio)
Перезагрузите ComfyU
или
скопируйте папку RaykoStudio_Nodes в папку: ComfyUI/custom_nodes/
Перезагрузите ComfyU
3. Убедитесь, что папка `fonts` содержит шрифты (`.ttf` или `.otf`). Вы можете добавить свои шрифты в эту папку. Если папка пуста или отсутствует, будет использоваться шрифт по умолчанию.
4. Установите необходимые зависимости, указанные в `requirements.txt`: pip install -r requirements.txt
Перезапустите ComfyUI, чтобы нода стала доступна.
## ⛓️‍💥 Зависимости
Для работы ноды требуются следующие библиотеки (указаны в requirements.txt):
torch>=1.7.0
numpy>=1.19.0
Pillow>=8.0.0
opencv-python>=4.5.0
## 🎛 Использование
После установки найдите ноду в ComfyUI под названием 🦊 RS_RusTextOverlay (категория: 🦊 RaykoStudio/Image).
Подключите входные данные (image и mask) и настройте параметры.
Пример рабочего процесса можно найти в папке examples:
- example workflow.json: Пример рабочего процесса для ComfyUI.
- example.png: Скриншот результата.
## 🤝 Сообщение об ошибках (Issues)
Если вы столкнулись с проблемой или нашли баг:
Проверьте раздел Issues на GitHub, возможно, проблема уже известна.
Если проблема новая, создайте новый Issue, описав:
- Версию ComfyUI и Python.
- Описание проблемы и шаги для ее воспроизведения.
- Скриншоты или логи ошибок (если есть).
## 📜 Лицензия
MIT License. Используйте ноду на свое усмотрение, но без каких-либо гарантий.
## ❤️ Благодарности
Спасибо сообществу ComfyUI за вдохновение и поддержку! Если вам понравилась эта нода, не забудьте поставить звездочку на GitHub!
-3
View File
@@ -1,3 +0,0 @@
from .nodes.russian_text import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 KiB

After

Width:  |  Height:  |  Size: 305 KiB

-10
View File
@@ -1,10 +0,0 @@
{
"name": "🦊 RS_RusTextOverlay",
"author": "RaykoStudio",
"version": "1.0.0",
"comfyui": "0.3.27",
"description": "A node for ComfyUI that allows you to overlay text on an image in an area defined by a mask, with support for text rotation, custom fonts, line breaks and transparency.",
"node_classes": ["RS_RusTextOverlay"],
"tags": ["image", "mask", "text", "overlay", "adjustment"],
"requirements": ["torch>=2.5.0"]
}
-11
View File
@@ -1,11 +0,0 @@
from .russian_text import RS_RusTextOverlay
NODE_CLASS_MAPPINGS = {
"RS_RusTextOverlay": RS_RusTextOverlay
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RS_RusTextOverlay": "🦊 RS Русский Текст"
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
-244
View File
@@ -1,244 +0,0 @@
# nodes/russian_text.py
import os
import torch
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import cv2
class RS_RusTextOverlay:
def __init__(self):
print("\033[93m🦊\033[0m \033[93mRaykoStudio - Russian Text Overlay Node \033[92mLOADED\033[0m")
@classmethod
def INPUT_TYPES(cls):
current_dir = os.path.dirname(os.path.abspath(__file__))
fonts_dir = os.path.join(os.path.dirname(current_dir), "fonts")
font_list = []
if os.path.exists(fonts_dir):
for f in os.listdir(fonts_dir):
if f.lower().endswith(('.ttf', '.otf')):
font_list.append(f)
if not font_list:
font_list = ["default"]
print("RS_RusTextOverlay: No fonts found, using default")
return {
"required": {
"image": ("IMAGE",),
"mask": ("MASK",),
"text": ("STRING", {
"default": "Текст\nс переносом\tи табуляцией",
"multiline": True
}),
"font_name": (font_list, {"default": font_list[0]}),
"text_color": ("COLOR", {"default": "#FFFFFF"}),
"text_opacity": ("INT", {"default": 100, "min": 0, "max": 100}),
"min_font_size": ("INT", {"default": 8, "min": 1, "max": 500}),
"padding": ("INT", {"default": 5, "min": 0, "max": 100}),
"vertical_align": (["top", "center", "bottom"], {"default": "center"}),
"horizontal_align": (["left", "center", "right"], {"default": "center"}),
"rotate_with_mask": ("BOOLEAN", {"default": True}),
},
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "apply_text"
CATEGORY = "🦊 RaykoStudio/Image"
def apply_text(self, image, mask, text, font_name, text_color, text_opacity, min_font_size, padding, vertical_align, horizontal_align, rotate_with_mask):
try:
# Конвертация tensor -> PIL
image_pil = Image.fromarray((image[0].cpu().numpy() * 255).astype(np.uint8), 'RGB')
mask_pil = Image.fromarray((mask[0].cpu().numpy() * 255).astype(np.uint8), 'L')
# Получаем область маски
bbox = mask_pil.getbbox()
if not bbox:
return (image,)
x1, y1, x2, y2 = bbox
mask_width = x2 - x1 - 2*padding
mask_height = y2 - y1 - 2*padding
# Загрузка шрифта
if font_name != "default":
font_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"fonts",
font_name
)
font = self.find_optimal_font(font_path, text, mask_width, mask_height, min_font_size)
else:
font = ImageFont.load_default()
# Преобразуем прозрачность (0-100%) в альфа-канал (0-255)
alpha = int((text_opacity / 100.0) * 255)
if text_color.startswith('#'):
text_color = text_color[1:]
r = int(text_color[0:2], 16)
g = int(text_color[2:4], 16)
b = int(text_color[4:6], 16)
text_color_rgba = (r, g, b, alpha)
# Создаем текстовый слой
text_layer = Image.new("RGBA", image_pil.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(text_layer)
# Разбиваем текст на строки
lines = text.split('\n')
line_heights = []
line_widths = []
for line in lines:
line = line.replace('\t', ' ')
text_bbox = draw.textbbox((0, 0), line, font=font)
line_widths.append(text_bbox[2] - text_bbox[0])
line_heights.append(text_bbox[3] - text_bbox[1])
total_text_height = sum(line_heights)
max_text_width = max(line_widths)
if rotate_with_mask:
mask_np = np.array(mask_pil)
contours, _ = cv2.findContours(mask_np, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
contour = max(contours, key=cv2.contourArea)
points = contour.reshape(-1, 2).astype(np.float32)
mean = np.mean(points, axis=0)
cov = np.cov(points.T)
eigenvalues, eigenvectors = np.linalg.eigh(cov)
main_axis = eigenvectors[:, np.argmax(eigenvalues)]
angle = np.arctan2(main_axis[1], main_axis[0]) * 180 / np.pi
leftmost = points[np.argmin(points[:, 0])]
rightmost = points[np.argmax(points[:, 0])]
direction_vector = rightmost - leftmost
direction_angle = np.arctan2(direction_vector[1], direction_vector[0]) * 180 / np.pi
angle_diff = (direction_angle - angle) % 360
if angle_diff > 90 and angle_diff < 270:
angle += 180
angle = angle % 360
if 90 < angle <= 270:
angle += 180
angle = angle % 360
angle = -angle
temp_size = int(max(max_text_width, total_text_height) * 3)
temp_text = Image.new("RGBA", (temp_size, temp_size), (0, 0, 0, 0))
temp_draw = ImageDraw.Draw(temp_text)
center_x, center_y = temp_size // 2, temp_size // 2
current_y = center_y - total_text_height // 2
for line, line_height, line_width in zip(lines, line_heights, line_widths):
line = line.replace('\t', ' ')
pos_x = center_x - line_width // 2
temp_draw.text(
(pos_x, current_y),
line,
font=font,
fill=text_color_rgba,
anchor="lt"
)
current_y += line_height
rotated_text = temp_text.rotate(
angle,
center=(center_x, center_y),
resample=Image.BICUBIC,
expand=False
)
rot_bbox = rotated_text.getbbox()
if rot_bbox:
rot_width = rot_bbox[2] - rot_bbox[0]
rot_height = rot_bbox[3] - rot_bbox[1]
mask_center_x = (x1 + x2) / 2
mask_center_y = (y1 + y2) / 2
paste_x = int(mask_center_x - rot_width / 2)
paste_y = int(mask_center_y - rot_height / 2)
text_layer.paste(
rotated_text.crop(rot_bbox),
(paste_x, paste_y),
rotated_text.crop(rot_bbox)
)
else:
print("Ошибка: не удалось определить bounding box повернутого текста")
else:
if vertical_align == "top":
current_y = y1 + padding
elif vertical_align == "bottom":
current_y = y2 - total_text_height - padding
else:
current_y = y1 + (mask_height - total_text_height) // 2 + padding
for line, line_height, line_width in zip(lines, line_heights, line_widths):
line = line.replace('\t', ' ')
pos_x = {
"left": x1 + padding,
"right": x2 - line_width - padding,
"center": x1 + (mask_width - line_width) // 2 + padding
}[horizontal_align]
draw.text(
(pos_x, current_y),
line,
font=font,
fill=text_color_rgba,
anchor="lt"
)
current_y += line_height
result = Image.alpha_composite(
image_pil.convert("RGBA"),
text_layer
).convert("RGB")
return (torch.from_numpy(np.array(result).astype(np.float32) / 255.0).unsqueeze(0),)
except Exception as e:
print(f"Ошибка в RS_RusTextOverlay: {str(e)}")
return (image,)
def find_optimal_font(self, font_path, text, max_width, max_height, min_size):
temp_img = Image.new("RGB", (max_width, max_height))
draw = ImageDraw.Draw(temp_img)
lines = text.split('\n')
for size in range(500, min_size - 1, -1):
try:
font = ImageFont.truetype(font_path, size)
fits = True
total_height = 0
max_line_width = 0
for line in lines:
line = line.replace('\t', ' ')
bbox = draw.textbbox((0, 0), line, font=font)
line_width = bbox[2] - bbox[0]
line_height = bbox[3] - bbox[1]
total_height += line_height
max_line_width = max(max_line_width, line_width)
if line_width > max_width:
fits = False
break
if fits and total_height <= max_height:
return font
except Exception as e:
print(f"RS_RusTextOverlay: Failed to load font {font_path} with size {size}: {str(e)}") # Отладка: ошибка загрузки шрифта
continue
return ImageFont.load_default()
NODE_CLASS_MAPPINGS = {
"RS_RusTextOverlay": RS_RusTextOverlay
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RS_RusTextOverlay": "🦊 RS RusTextOverlay"
}
-16
View File
@@ -1,16 +0,0 @@
{
"name": "comfyui-rs-text-overlay-node",
"version": "1.0.0",
"description": "A node for ComfyUI that allows you to overlay text on an image in an area defined by a mask, with support for text rotation, custom fonts, line breaks and transparency.",
"author": "Raykosan",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/Raykosan/ComfyUI_RaykoStudio"
},
"comfyui": {
"nodes": [
"nodes/russian_text.py"
]
}
}
-4
View File
@@ -1,4 +0,0 @@
torch>=2.0.0
Pillow>=8.0.0
numpy>=1.19.0
opencv-python-headless>=4.2.0
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 KiB