From 11be99083caecdaf73f3752ccfd391147cb0c58f Mon Sep 17 00:00:00 2001 From: Kevin Yuan Date: Sun, 17 Nov 2024 19:45:56 +0800 Subject: [PATCH] initial checkin --- KY_SaveImageToPath.py | 85 +++++++++++++++++++ __init__.py | 13 +++ advanced_lying_sigma_sampler.py | 143 ++++++++++++++++++++++++++++++++ 3 files changed, 241 insertions(+) create mode 100644 KY_SaveImageToPath.py create mode 100644 __init__.py create mode 100644 advanced_lying_sigma_sampler.py diff --git a/KY_SaveImageToPath.py b/KY_SaveImageToPath.py new file mode 100644 index 0000000..09bc8b5 --- /dev/null +++ b/KY_SaveImageToPath.py @@ -0,0 +1,85 @@ +import os +import folder_paths +import json +import torch +from PIL import Image +import numpy as np +import json + +from nodes import SaveImage + + +class KY_SaveImageToPath(SaveImage): + @classmethod + def INPUT_TYPES(s): + return {"required": + {"IMG": ("IMAGE",), + "path": ("STRING", {"default": "ComfyUI.png"})}, + "hidden": + {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"} + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "save_image_to_path" + OUTPUT_NODE = True + CATEGORY = "KY Nodes/Image" + + def save_image_to_path(self, IMG, path="ComfyUI.png", prompt=None, extra_pnginfo=None): + #results = self.save_images(images, filename_prefix, prompt, extra_pnginfo) + saved_paths = [] + #folder_structure = [] + #folder_structure = json.loads(folder_structure) + base_directory = path + full_file_path = path + images = IMG + + # Ensure base directory exists + #os.makedirs(base_directory, exist_ok=True) + + for i, image in enumerate(images): + # Convert the image tensor to a PIL Image + img = Image.fromarray((image.cpu().numpy() * 255).astype(np.uint8)) + + # Create the full folder path based on the folder structure + #full_folder_path = self.create_folder_path(base_directory, []) + #os.makedirs(full_folder_path, exist_ok=True) + + # Create the file name and ensure it doesn't overwrite existing files + #index = 0 + i + #while True: + #full_file_name = file_name_template.format(index=index) + # full_file_path = path #os.path.join(full_folder_path, full_file_name) + # if not os.path.exists(full_file_path): + # break + # index += 1 + + # Save the image + img.save(full_file_path) + + # Save metadata if provided + #if metadata: + # metadata_file_name = f"{os.path.splitext(full_file_name)[0]}_metadata.txt" + # metadata_file_path = os.path.join(full_folder_path, metadata_file_name) + # with open(metadata_file_path, 'w') as f: + # f.write(metadata) + + saved_paths.append(path) + break + #return (", ".join(saved_paths),) + #results = (", ".join(saved_paths)) + return { + "ui": { + #"images": results['ui']['IMG'] + "images": path + }, + "result": (IMG,) + } + def create_folder_path(self, base_directory, folder_structure): + path = base_directory + for folder in folder_structure: + path = os.path.join(path, folder['name']) + for child in folder['children']: + path = self.create_folder_path(path, [child]) + return path + + diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..80c74dd --- /dev/null +++ b/__init__.py @@ -0,0 +1,13 @@ +from .advanced_lying_sigma_sampler import AdvancedLyingSigmaSamplerNode +from .KY_SaveImageToPath import KY_SaveImageToPath + +# 将节点映射到NODE_CLASS_MAPPINGS和NODE_DISPLAY_NAME_MAPPINGS +NODE_CLASS_MAPPINGS = { + "AdvancedLyingSigmaSampler": AdvancedLyingSigmaSamplerNode, + "KY_SaveImageToPath": KY_SaveImageToPath +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "AdvancedLyingSigmaSampler": "Advanced Lying Sigma Sampler", + "KY_SaveImageToPath": "Save Image To target Path" +} \ No newline at end of file diff --git a/advanced_lying_sigma_sampler.py b/advanced_lying_sigma_sampler.py new file mode 100644 index 0000000..0d5b6a2 --- /dev/null +++ b/advanced_lying_sigma_sampler.py @@ -0,0 +1,143 @@ +# Based on the concept from https://github.com/muerrilla/sd-webui-detail-daemon +# Original code courtesy from https://github.com/blepping/ +from __future__ import annotations + +from typing import TYPE_CHECKING +import inspect + +from comfy.samplers import KSAMPLER +import numpy as np + +if TYPE_CHECKING: + import torch + +def advanced_lying_sigma_sampler( + model: object, + x: torch.Tensor, + sigmas: torch.Tensor, + *, + sampler: object, + dishonesty_factor: float, + start_percent: float, + end_percent: float, + smooth_factor: float = 0.5, + **kwargs: dict, +) -> torch.Tensor: + start_sigma = round(model.inner_model.inner_model.model_sampling.percent_to_sigma(start_percent), 4) + end_sigma = round(model.inner_model.inner_model.model_sampling.percent_to_sigma(end_percent), 4) + + def model_wrapper(x: torch.Tensor, sigma: torch.Tensor, **extra_args: dict): + sigma_float = float(sigma.max().detach().cpu()) + if end_sigma <= sigma_float <= start_sigma: + adjustment = dishonesty_factor * (0.5 * (1 - np.cos(smooth_factor * np.pi))) + sigma = sigma * (1.0 + adjustment) + return model(x, sigma, **extra_args) + + for k in ( + "inner_model", + "sigmas", + ): + if hasattr(model, k): + setattr(model_wrapper, k, getattr(model, k)) + + # Check if denoise_mask is supported by the sampler function + if 'denoise_mask' in inspect.signature(sampler.sampler_function).parameters: + return sampler.sampler_function( + model_wrapper, + x, + sigmas, + denoise_mask=kwargs.get('denoise_mask'), + **kwargs, + **sampler.extra_options, + ) + else: + return sampler.sampler_function( + model_wrapper, + x, + sigmas, + **kwargs, + **sampler.extra_options, + ) + +class AdvancedLyingSigmaSamplerNode: + DESCRIPTION = "高级sigma控制器,使用前最好关闭float rounding并重启comfui,以达到最佳控制效果|For advanced sigma controllers, it is best to turn off float rounding and restart comfui before use for optimal control." + CATEGORY = "KY Nodes/advanced_samplers" + RETURN_TYPES = ("SAMPLER",) + FUNCTION = "initialize" + + @classmethod + def INPUT_TYPES(cls) -> dict: + return { + "required": { + "sampler": ("SAMPLER",), + "dishonesty_factor": ( + "FLOAT", + { + "default": -0.1, + "min": -1.0, + "max": 1.0, + "step": 0.001, + "tooltip": "默认值-0.1,一般来说这是一个较大值|Default value -0.1, which is generally a larger value." + } + ), + "start_percent": ( + "FLOAT", + { + "default": 0.1, + "min": 0.0, + "max": 1.0, + "step": 0.01, + "tooltip": "bilibili@深深蓝hana." + } + ), + "end_percent": ( + "FLOAT", + { + "default": 0.9, + "min": 0.0, + "max": 1.0, + "step": 0.01, + "tooltip": "bilibili@深深蓝hana." + } + ), + "smooth_factor": ( + "FLOAT", + { + "default": 0.5, + "min": 0.0, + "max": 1.0, + "step": 0.01, + "tooltip": "bilibili@深深蓝hana." + } + ), + } + } + + @classmethod + def initialize( + cls, + sampler: object, + dishonesty_factor: float, + start_percent: float, + end_percent: float, + smooth_factor: float, + ) -> tuple: + return ( + KSAMPLER( + advanced_lying_sigma_sampler, + extra_options={ + "sampler": sampler, + "dishonesty_factor": dishonesty_factor, + "start_percent": start_percent, + "end_percent": end_percent, + "smooth_factor": smooth_factor, + }, + ), + ) + +NODE_CLASS_MAPPINGS = { + "AdvancedLyingSigmaSampler": AdvancedLyingSigmaSamplerNode, +} +NODE_DISPLAY_NAME_MAPPINGS = { + "AdvancedLyingSigmaSampler": "Advanced Lying Sigma Sampler" +}