Update
This commit is contained in:
+14
-6
@@ -1,19 +1,20 @@
|
||||
from .meyo_node_Computational import StandardSize, CompareInt, FloatToInteger, GenerateNumbers, GetRandomIntegerInRange
|
||||
from .meyo_node_String import AddPrefixSuffix, ExtractSubstring, ExtractSubstringByIndices, SplitStringByDelimiter, ProcessString, ExtractBeforeAfter, ReplaceNthOccurrence, BatchReplaceStrings, ReplaceMultiple, RandomLineFromText, CheckSubstringPresence, AddPrefixSuffixToLines, ExtractAndCombineLines, FilterLinesBySubstrings, FilterLinesByWordCount, SplitAndExtractText, CountOccurrences, ExtractLinesByIndex, ExtractSpecificLines, RemoveContentBetweenChars, ShuffleTextLines, ConditionalTextOutput, TextConditionCheck, TextConcatenation, ExtractSpecificData, FindFirstLineContent
|
||||
from .meyo_node_File import FileListAndSuffix, ReadExcelData, WriteExcelData, FindExcelData, ReadExcelRowOrColumnDiff
|
||||
from .meyo_node_Computational import CompareInt, FloatToInteger, GenerateNumbers, GetRandomIntegerInRange
|
||||
from .meyo_node_String import SingleTextInput, MultiParamInputNode, AddPrefixSuffix, ExtractSubstring, ExtractSubstringByIndices, SplitStringByDelimiter, ProcessString, ExtractBeforeAfter, ReplaceNthOccurrence, BatchReplaceStrings, ReplaceMultiple, RandomLineFromText, CheckSubstringPresence, AddPrefixSuffixToLines, ExtractAndCombineLines, FilterLinesBySubstrings, FilterLinesByWordCount, SplitAndExtractText, CountOccurrences, ExtractLinesByIndex, ExtractSpecificLines, RemoveContentBetweenChars, ShuffleTextLines, ConditionalTextOutput, TextConditionCheck, TextConcatenation, ExtractSpecificData, FindFirstLineContent
|
||||
from .meyo_node_File import LoadAndAdjustImage, ImageAdjuster, SaveImagEX, FileListAndSuffix, ReadExcelData, WriteExcelData, FindExcelData, ReadExcelRowOrColumnDiff
|
||||
from .meyo_node_Functional import GetCurrentTime, SelectionParameter
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
#运算型节点:meyo_node_Computational
|
||||
"StandardSize": StandardSize,
|
||||
"CompareInt": CompareInt,
|
||||
"FloatToInteger": FloatToInteger,
|
||||
"GenerateNumbers": GenerateNumbers,
|
||||
"GetRandomIntegerInRange": GetRandomIntegerInRange,
|
||||
|
||||
#字符串处理:meyo_node_String
|
||||
"SingleTextInput": SingleTextInput,
|
||||
"MultiParamInputNode": MultiParamInputNode,
|
||||
"AddPrefixSuffix": AddPrefixSuffix,
|
||||
"ExtractSubstring": ExtractSubstring,
|
||||
"ExtractSubstringByIndices": ExtractSubstringByIndices,
|
||||
@@ -42,6 +43,9 @@ NODE_CLASS_MAPPINGS = {
|
||||
"FindFirstLineContent": FindFirstLineContent,
|
||||
|
||||
#文件处理:meyo_node_File
|
||||
"LoadAndAdjustImage": LoadAndAdjustImage,
|
||||
"ImageAdjuster": ImageAdjuster,
|
||||
"SaveImagEX": SaveImagEX,
|
||||
"FileListAndSuffix": FileListAndSuffix,
|
||||
"ReadExcelData": ReadExcelData,
|
||||
"WriteExcelData": WriteExcelData,
|
||||
@@ -58,13 +62,14 @@ NODE_CLASS_MAPPINGS = {
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
|
||||
#运算型节点:meyo_node_Computational
|
||||
"StandardSize": "重置尺寸(meeeyo.com)",
|
||||
"CompareInt": "比较数值(meeeyo.com)",
|
||||
"FloatToInteger": "规范数值(meeeyo.com)",
|
||||
"GenerateNumbers": "生成范围数组(meeeyo.com)",
|
||||
"GetRandomIntegerInRange": "范围内随机数(meeeyo.com)",
|
||||
|
||||
#字符串处理:meyo_node_String
|
||||
"SingleTextInput": "文本输入(meeeyo.com)",
|
||||
"MultiParamInputNode": "多参数输入(meeeyo.com)",
|
||||
"AddPrefixSuffix": "添加前后缀(meeeyo.com)",
|
||||
"ExtractSubstring": "提取标签之间(meeeyo.com)",
|
||||
"ExtractSubstringByIndices": "按数字范围提取(meeeyo.com)",
|
||||
@@ -93,6 +98,9 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"FindFirstLineContent": "指定字符行参数(meeeyo.com)",
|
||||
|
||||
#文件处理:meyo_node_File
|
||||
"LoadAndAdjustImage": "加载重置图像(meeeyo.com)",
|
||||
"ImageAdjuster": "重置图像(meeeyo.com)",
|
||||
"SaveImagEX": "保存图像(meeeyo.com)",
|
||||
"FileListAndSuffix": "从路径加载(meeeyo.com)",
|
||||
"ReadExcelData": "读取表格数据(meeeyo.com)",
|
||||
"WriteExcelData": "写入表格数据(meeeyo.com)",
|
||||
@@ -100,7 +108,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ReadExcelRowOrColumnDiff": "读取表格数量差(meeeyo.com)",
|
||||
|
||||
#功能型节点:meyo_node_Functional
|
||||
"GetCurrentTime": "当前时间",
|
||||
"GetCurrentTime": "当前时间(戳)",
|
||||
"SelectionParameter": "选择参数",
|
||||
|
||||
}
|
||||
|
||||
@@ -2,52 +2,6 @@ import math
|
||||
import random
|
||||
|
||||
|
||||
#======宽度高度调整
|
||||
class StandardSize:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"width": ("INT",),
|
||||
"height": ("INT",),
|
||||
"size_option": (["小尺寸", "中尺寸", "大尺寸"], {"default": "中尺寸"}), # 下拉选择框
|
||||
},
|
||||
"optional": {},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
FUNCTION = "resize_image"
|
||||
CATEGORY = "Meeeyo/Image"
|
||||
|
||||
def resize_image(self, width, height, size_option):
|
||||
# 根据选择的选项调整尺寸像尺寸
|
||||
if size_option == "小尺寸":
|
||||
if width / height >= 1.23:
|
||||
return (768, 512)
|
||||
elif 0.82 < width / height < 1.23:
|
||||
return (768, 768)
|
||||
else:
|
||||
return (512, 768)
|
||||
elif size_option == "中尺寸":
|
||||
if width / height >= 1.23:
|
||||
return (1216, 832)
|
||||
elif 0.82 < width / height < 1.23:
|
||||
return (1216, 1216)
|
||||
else:
|
||||
return (832, 1216)
|
||||
elif size_option == "大尺寸":
|
||||
if width / height >= 1.23:
|
||||
return (1600, 1120)
|
||||
elif 0.82 < width / height < 1.23:
|
||||
return (1600, 1600)
|
||||
else:
|
||||
return (1120, 1600)
|
||||
|
||||
|
||||
|
||||
#======比较数值
|
||||
class CompareInt:
|
||||
def __init__(self):
|
||||
|
||||
+246
-1
@@ -1,6 +1,251 @@
|
||||
import os
|
||||
import csv
|
||||
import openpyxl
|
||||
import os
|
||||
import pathlib
|
||||
import folder_paths
|
||||
import torch
|
||||
import node_helpers
|
||||
import numpy as np
|
||||
from PIL import Image, ImageOps, ImageSequence
|
||||
|
||||
|
||||
#======加载重置图像
|
||||
class LoadAndAdjustImage:
|
||||
_color_channels = ["alpha", "red", "green", "blue"]
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
files = [f.name for f in pathlib.Path(input_dir).iterdir() if f.is_file()]
|
||||
return {
|
||||
"required": {
|
||||
"image": (sorted(files), {"image_upload": True}),
|
||||
"max_dimension": ("INT", {"default": 1024, "min": 0, "max": 4096, "step": 8}),
|
||||
"size_option": (["Custom", "Small", "Medium", "Large"], {"default": "Custom"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "MASK", "INT", "INT")
|
||||
RETURN_NAMES = ("image", "mask", "width", "height")
|
||||
FUNCTION = "load_image"
|
||||
CATEGORY = "Meeeyo/File"
|
||||
|
||||
def load_image(self, image, max_dimension, size_option):
|
||||
image_path = folder_paths.get_annotated_filepath(image)
|
||||
img = node_helpers.pillow(Image.open, image_path)
|
||||
|
||||
W, H = img.size
|
||||
output_images = []
|
||||
output_masks = []
|
||||
|
||||
for frame in ImageSequence.Iterator(img):
|
||||
frame = node_helpers.pillow(ImageOps.exif_transpose, frame)
|
||||
|
||||
if frame.mode == 'P':
|
||||
frame = frame.convert("RGBA")
|
||||
elif 'A' in frame.getbands():
|
||||
frame = frame.convert("RGBA")
|
||||
|
||||
if size_option != "Custom":
|
||||
aspect_ratio = W / H
|
||||
|
||||
if size_option == "Small":
|
||||
if aspect_ratio >= 1.23:
|
||||
target_width, target_height = 768, 512
|
||||
elif 0.82 < aspect_ratio < 1.23:
|
||||
target_width, target_height = 768, 768
|
||||
else:
|
||||
target_width, target_height = 512, 768
|
||||
elif size_option == "Medium":
|
||||
if aspect_ratio >= 1.23:
|
||||
target_width, target_height = 1216, 832
|
||||
elif 0.82 < aspect_ratio < 1.23:
|
||||
target_width, target_height = 1216, 1216
|
||||
else:
|
||||
target_width, target_height = 832, 1216
|
||||
elif size_option == "Large":
|
||||
if aspect_ratio >= 1.23:
|
||||
target_width, target_height = 1600, 1120
|
||||
elif 0.82 < aspect_ratio < 1.23:
|
||||
target_width, target_height = 1600, 1600
|
||||
else:
|
||||
target_width, target_height = 1120, 1600
|
||||
|
||||
image = frame.convert("RGB")
|
||||
image = ImageOps.fit(image, (target_width, target_height), method=Image.Resampling.BILINEAR, centering=(0.5, 0.5))
|
||||
|
||||
if 'A' in frame.getbands():
|
||||
mask = np.array(frame.getchannel('A')).astype(np.float32) / 255.0
|
||||
mask = torch.from_numpy(mask)
|
||||
mask_image = Image.fromarray((mask.numpy() * 255).astype(np.uint8))
|
||||
mask_image = ImageOps.fit(mask_image, (target_width, target_height), method=Image.Resampling.BILINEAR, centering=(0.5, 0.5))
|
||||
mask = np.array(mask_image).astype(np.float32) / 255.0
|
||||
mask = torch.from_numpy(mask)
|
||||
else:
|
||||
mask = torch.ones((target_height, target_width), dtype=torch.float32, device="cpu")
|
||||
else:
|
||||
ratio = min(max_dimension / W, max_dimension / H)
|
||||
adjusted_width = round(W * ratio)
|
||||
adjusted_height = round(H * ratio)
|
||||
|
||||
image = frame.convert("RGB")
|
||||
image = image.resize((adjusted_width, adjusted_height), Image.Resampling.BILINEAR)
|
||||
|
||||
if 'A' in frame.getbands():
|
||||
mask = np.array(frame.getchannel('A')).astype(np.float32) / 255.0
|
||||
mask = torch.from_numpy(mask)
|
||||
mask_image = Image.fromarray((mask.numpy() * 255).astype(np.uint8))
|
||||
mask_image = mask_image.resize((adjusted_width, adjusted_height), Image.Resampling.BILINEAR)
|
||||
mask = np.array(mask_image).astype(np.float32) / 255.0
|
||||
mask = torch.from_numpy(mask)
|
||||
else:
|
||||
mask = torch.ones((adjusted_height, adjusted_width), dtype=torch.float32, device="cpu")
|
||||
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = torch.from_numpy(image)[None,]
|
||||
output_images.append(image)
|
||||
output_masks.append(mask.unsqueeze(0))
|
||||
|
||||
if len(output_images) > 1:
|
||||
output_image = torch.cat(output_images, dim=0)
|
||||
output_mask = torch.cat(output_masks, dim=0)
|
||||
else:
|
||||
output_image = output_images[0]
|
||||
output_mask = output_masks[0]
|
||||
if size_option != "Custom":
|
||||
return (output_image, output_mask, target_width, target_height)
|
||||
else:
|
||||
return (output_image, output_mask, adjusted_width, adjusted_height)
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(s, image):
|
||||
if not folder_paths.exists_annotated_filepath(image):
|
||||
return f"Invalid image file: {image}"
|
||||
return True
|
||||
|
||||
|
||||
#======重置图像尺寸
|
||||
class ImageAdjuster:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"max_dimension": ("INT", {"default": 1024, "min": 0, "max": 4096, "step": 8}),
|
||||
"size_option": (["Custom", "Small", "Medium", "Large"], {"default": "Custom"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "INT", "INT")
|
||||
RETURN_NAMES = ("image", "width", "height")
|
||||
FUNCTION = "process_image"
|
||||
CATEGORY = "Meeeyo/File"
|
||||
|
||||
def process_image(self, image, max_dimension=1024, size_option="Custom"):
|
||||
input_image = Image.fromarray((image.squeeze(0).numpy() * 255).astype(np.uint8))
|
||||
W, H = input_image.size
|
||||
|
||||
processed_images = []
|
||||
|
||||
for frame in [input_image]:
|
||||
frame = ImageOps.exif_transpose(frame)
|
||||
|
||||
if frame.mode == 'P':
|
||||
frame = frame.convert("RGBA")
|
||||
elif 'A' in frame.getbands():
|
||||
frame = frame.convert("RGBA")
|
||||
|
||||
if size_option != "Custom":
|
||||
aspect_ratio = W / H
|
||||
|
||||
if size_option == "Small":
|
||||
if aspect_ratio >= 1.23:
|
||||
target_width, target_height = 768, 512
|
||||
elif 0.82 < aspect_ratio < 1.23:
|
||||
target_width, target_height = 768, 768
|
||||
else:
|
||||
target_width, target_height = 512, 768
|
||||
elif size_option == "Medium":
|
||||
if aspect_ratio >= 1.23:
|
||||
target_width, target_height = 1216, 832
|
||||
elif 0.82 < aspect_ratio < 1.23:
|
||||
target_width, target_height = 1216, 1216
|
||||
else:
|
||||
target_width, target_height = 832, 1216
|
||||
elif size_option == "Large":
|
||||
if aspect_ratio >= 1.23:
|
||||
target_width, target_height = 1600, 1120
|
||||
elif 0.82 < aspect_ratio < 1.23:
|
||||
target_width, target_height = 1600, 1600
|
||||
else:
|
||||
target_width, target_height = 1120, 1600
|
||||
processed_image = frame.convert("RGB")
|
||||
processed_image = ImageOps.fit(processed_image, (target_width, target_height), method=Image.Resampling.BILINEAR, centering=(0.5, 0.5))
|
||||
else:
|
||||
ratio = min(max_dimension / W, max_dimension / H)
|
||||
adjusted_width = round(W * ratio)
|
||||
adjusted_height = round(H * ratio)
|
||||
|
||||
processed_image = frame.convert("RGB")
|
||||
processed_image = processed_image.resize((adjusted_width, adjusted_height), Image.Resampling.BILINEAR)
|
||||
|
||||
processed_image = np.array(processed_image).astype(np.float32) / 255.0
|
||||
processed_image = torch.from_numpy(processed_image)[None,]
|
||||
processed_images.append(processed_image)
|
||||
|
||||
output_image = torch.cat(processed_images, dim=0) if len(processed_images) > 1 else processed_images[0]
|
||||
if size_option != "Custom":
|
||||
return (output_image, target_width, target_height)
|
||||
else:
|
||||
return (output_image, adjusted_width, adjusted_height)
|
||||
|
||||
#======保存图像
|
||||
class SaveImagEX:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"save_path": ("STRING", {"default": "./output"}),
|
||||
"image_name": ("STRING", {"default": "ComfyUI"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image",)
|
||||
FUNCTION = "save_image"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "Meeeyo/File"
|
||||
|
||||
def save_image(self, image, save_path, image_name):
|
||||
if not isinstance(image, torch.Tensor):
|
||||
raise ValueError("Invalid image tensor format")
|
||||
if save_path == "./output":
|
||||
save_path = self.output_dir
|
||||
elif not os.path.isabs(save_path):
|
||||
save_path = os.path.join(self.output_dir, save_path)
|
||||
os.makedirs(save_path, exist_ok=True)
|
||||
base_name = os.path.splitext(image_name)[0]
|
||||
batch_size = image.shape[0]
|
||||
channel_to_mode = {1: "L", 3: "RGB", 4: "RGBA"}
|
||||
|
||||
for i in range(batch_size):
|
||||
filename = f"{base_name}.png" if batch_size == 1 else f"{base_name}_{i:05d}.png"
|
||||
full_path = os.path.join(save_path, filename)
|
||||
single_image = image[i].cpu().numpy()
|
||||
single_image = (single_image * 255).astype('uint8')
|
||||
channels = single_image.shape[2]
|
||||
if channels not in channel_to_mode:
|
||||
raise ValueError(f"Unsupported channel number: {channels}")
|
||||
mode = channel_to_mode[channels]
|
||||
if channels == 1:
|
||||
single_image = single_image[:, :, 0]
|
||||
pil_image = Image.fromarray(single_image, mode)
|
||||
pil_image.save(full_path, format="PNG", compress_level=0)
|
||||
return (image,)
|
||||
|
||||
|
||||
#======文件路径和后缀统计
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from datetime import datetime
|
||||
import ntplib
|
||||
import datetime
|
||||
import time
|
||||
|
||||
#======获取当前时间
|
||||
class GetCurrentTime:
|
||||
@@ -17,7 +17,7 @@ class GetCurrentTime:
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_TYPES = ("STRING", "INT") # 新增 INTEGER 类型的返回值
|
||||
FUNCTION = "get_current_time"
|
||||
CATEGORY = "Meeeyo/Functional"
|
||||
|
||||
@@ -26,13 +26,15 @@ class GetCurrentTime:
|
||||
|
||||
def get_current_time(self, prefix="", any=None):
|
||||
# Get the current system time
|
||||
current_time = datetime.now()
|
||||
current_time = datetime.datetime.now()
|
||||
# Format the time as a string
|
||||
formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
# Include the prefix in the output
|
||||
result = f"{prefix}{formatted_time}"
|
||||
# Return the formatted time with prefix as a string
|
||||
return (result,)
|
||||
# Get the current timestamp as an integer with milliseconds precision
|
||||
timestamp = int(round(time.time() * 1000)) # 获取当前时间戳的整数部分并精确到毫秒
|
||||
# Return both the formatted time with prefix and the timestamp
|
||||
return (result, timestamp) # 返回两个值:字符串和整数
|
||||
|
||||
|
||||
#======选择性别和模板
|
||||
|
||||
@@ -7,6 +7,47 @@ import math
|
||||
import re
|
||||
|
||||
|
||||
#======多参数输入
|
||||
class SingleTextInput:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"text": ("STRING", {"default": "", "multiline": True}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "process_input"
|
||||
CATEGORY = "Meeeyo/Text"
|
||||
OUTPUT_NODE = False
|
||||
|
||||
def process_input(self, text):
|
||||
return (text,)
|
||||
|
||||
|
||||
#======多参数输入
|
||||
class MultiParamInputNode:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"text1": ("STRING", {"default": "", "multiline": True}), # 第一个多行文本输入框
|
||||
"text2": ("STRING", {"default": "", "multiline": True}), # 第二个多行文本输入框
|
||||
"int1": ("INT", {"default": 0, "min": -1000000, "max": 1000000}), # 第一个整数输入框
|
||||
"int2": ("INT", {"default": 0, "min": -1000000, "max": 1000000}), # 第二个整数输入框
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "INT", "INT") # 返回两个字符串和两个整数
|
||||
FUNCTION = "process_inputs"
|
||||
CATEGORY = "Meeeyo/String"
|
||||
OUTPUT_NODE = False
|
||||
|
||||
def process_inputs(self, text1, text2, int1, int2):
|
||||
return (text1, text2, int1, int2)
|
||||
|
||||
|
||||
#======添加前后缀
|
||||
class AddPrefixSuffix:
|
||||
def __init__(self):
|
||||
|
||||
Reference in New Issue
Block a user