Changes in document structure
This commit is contained in:
+5
-12
@@ -4,22 +4,15 @@ import yaml
|
||||
import os
|
||||
import folder_paths
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
|
||||
node_list = [
|
||||
"server",
|
||||
"api",
|
||||
"easyNodes",
|
||||
"image",
|
||||
"logic",
|
||||
"deprecated",
|
||||
]
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
|
||||
for module_name in node_list:
|
||||
imported_module = importlib.import_module(".py.{}".format(module_name), __name__)
|
||||
importlib.import_module('.py.api', __name__)
|
||||
importlib.import_module('.py.server', __name__)
|
||||
nodes_list = ["util", "seed", "prompt", "loaders", "adapter", "inpaint", "preSampling", "samplers", "fix", "pipe", "xyplot", "image", "logic", "api", "deprecated"]
|
||||
for module_name in nodes_list:
|
||||
imported_module = importlib.import_module(".py.nodes.{}".format(module_name), __name__)
|
||||
NODE_CLASS_MAPPINGS = {**NODE_CLASS_MAPPINGS, **imported_module.NODE_CLASS_MAPPINGS}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {**NODE_DISPLAY_NAME_MAPPINGS, **imported_module.NODE_DISPLAY_NAME_MAPPINGS}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from .libs.loader import easyLoader
|
||||
from .libs.sampler import easySampler
|
||||
|
||||
sampler = easySampler()
|
||||
easyCache = easyLoader()
|
||||
|
||||
|
||||
@@ -299,6 +299,3 @@ async def download_model(request):
|
||||
return web.Response(status=200)
|
||||
except:
|
||||
return web.Response(status=500)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
+3
-1
@@ -390,4 +390,6 @@ PROMPT_TEMPLATE = {
|
||||
"environment": ["sunshine from window", "neon night, city", "sunset over sea", "golden time", "sci-fi RGB glowing, cyberpunk", "natural lighting", "warm atmosphere, at home, bedroom", "magic lit", "evil, gothic, in a cave", "light and shadow", "shadow from window", "soft studio lighting", "home atmosphere, cozy bedroom illumination", "neon, Wong Kar-wai, warm", "moonlight through curtains", "stormy sky lighting", "underwater glow, deep sea", "foggy forest at dawn", "golden hour in a meadow", "rainbow reflections, neon", "cozy candlelight", "apocalyptic, smoky atmosphere", "red glow, emergency lights", "mystical glow, enchanted forest", "campfire light", "harsh, industrial lighting", "sunrise in the mountains", "evening glow in the desert", "moonlight in a dark alley", "golden glow at a fairground", "midnight in the forest", "purple and pink hues at twilight", "foggy morning, muted light", "candle-lit room, rustic vibe", "fluorescent office lighting", "lightning flash in storm", "night, cozy warm light from fireplace", "ethereal glow, magical forest", "dusky evening on a beach", "afternoon light filtering through trees", "blue neon light, urban street", "red and blue police lights in rain", "aurora borealis glow, arctic landscape", "sunrise through foggy mountains", "golden hour on a city skyline", "mysterious twilight, heavy mist", "early morning rays, forest clearing", "colorful lantern light at festival", "soft glow through stained glass", "harsh spotlight in dark room", "mellow evening glow on a lake", "crystal reflections in a cave", "vibrant autumn lighting in a forest", "gentle snowfall at dusk", "hazy light of a winter morning", "soft, diffused foggy glow", "underwater luminescence", "rain-soaked reflections in city lights", "golden sunlight streaming through trees", "fireflies lighting up a summer night", "glowing embers from a forge", "dim candlelight in a gothic castle", "midnight sky with bright starlight", "warm sunset in a rural village", "flickering light in a haunted house", "desert sunset with mirage-like glow", "golden beams piercing through storm clouds"],
|
||||
"background": ["cars and people", "a cozy bed and a lamp", "a forest clearing with mist", "a bustling marketplace", "a quiet beach at dusk", "an old, cobblestone street", "a futuristic cityscape", "a tranquil lake with mountains", "a mysterious cave entrance", "bookshelves and plants in the background", "an ancient temple in ruins", "tall skyscrapers and neon signs", "a starry sky over a desert", "a bustling café", "rolling hills and farmland", "a modern living room with a fireplace", "an abandoned warehouse", "a picturesque mountain range", "a starry night sky", "the interior of a futuristic spaceship", "the cluttered workshop of an inventor", "the glowing embers of a bonfire", "a misty lake surrounded by trees", "an ornate palace hall", "a busy street market", "a vast desert landscape", "a peaceful library corner", "bustling train station", "a mystical, enchanted forest", "an underwater reef with colorful fish", "a quiet rural village", "a sandy beach with palm trees", "a vibrant coral reef, teeming with life", "snow-capped mountains in distance", "a stormy ocean, waves crashing", "a rustic barn in open fields", "a futuristic lab with glowing screens", "a dark, abandoned castle", "the ruins of an ancient civilization", "a bustling urban street in rain", "an elegant grand ballroom", "a sprawling field of wildflowers", "a dense jungle with sunlight filtering through", "a dimly lit, vintage bar", "an ice cave with sparkling crystals", "a serene riverbank at sunset", "a narrow alley with graffiti walls", "a peaceful zen garden with koi pond", "a high-tech control room", "a quiet mountain village at dawn", "a lighthouse on a rocky coast", "a rainy street with flickering lights", "a frozen lake with ice formations", "an abandoned theme park", "a small fishing village on a pier", "rolling sand dunes in a desert", "a dense forest with towering redwoods", "a snowy cabin in the mountains", "a mystical cave with bioluminescent plants", "a castle courtyard under moonlight", "a bustling open-air night market", "an old train station with steam", "a tranquil waterfall surrounded by trees", "a vineyard in the countryside", "a quaint medieval village", "a bustling harbor with boats", "a high-tech futuristic mall", "a lush tropical rainforest"],
|
||||
"nsfw": ["nude", "breast", "small breast", "middle breast", "large breast", "nipples", "clothes lift", "pussy juice trail", "pussy juice puddle", "small testicles", "medium testicles", "large testicles", "disembodied penis", "cum on body", "cum inside", "cum outside", "fingering", "handjob", "fellatio", "licking penis", "paizuri", "doggystyle", "cowgirl", "reversed cowgirl", "piledriver", "suspended congress", "full nelson",],
|
||||
}
|
||||
}
|
||||
|
||||
NEW_SCHEDULERS = ['align_your_steps', 'gits']
|
||||
-7752
File diff suppressed because it is too large
Load Diff
@@ -14,9 +14,9 @@ class easyControlnet:
|
||||
return (positive, negative)
|
||||
|
||||
# kolors controlnet patch
|
||||
from ..kolors.loader import is_kolors_model, applyKolorsUnet
|
||||
from py.modules.kolors import is_kolors_model, applyKolorsUnet
|
||||
if is_kolors_model(model):
|
||||
from ..kolors.model_patch import patch_controlnet
|
||||
from py.modules.kolors import patch_controlnet
|
||||
if control_net is None:
|
||||
with applyKolorsUnet():
|
||||
control_net = easyCache.load_controlnet(control_net_name, scale_soft_weights, use_cache)
|
||||
|
||||
+5
-49
@@ -8,7 +8,7 @@ from comfy.model_patcher import ModelPatcher
|
||||
from nodes import NODE_CLASS_MAPPINGS
|
||||
from collections import defaultdict
|
||||
from .log import log_node_info, log_node_error
|
||||
from ..dit.pixArt.loader import load_pixart
|
||||
from ..modules.dit.pixArt.loader import load_pixart
|
||||
|
||||
diffusion_loaders = ["easy fullLoader", "easy a1111Loader", "easy fluxLoader", "easy comfyLoader", "easy hunyuanDiTLoader", "easy zero123Loader", "easy svdLoader"]
|
||||
stable_cascade_loaders = ["easy cascadeLoader"]
|
||||
@@ -240,7 +240,7 @@ class easyLoader:
|
||||
else:
|
||||
model_options = {}
|
||||
if re.search("nf4", ckpt_name):
|
||||
from ..bitsandbytes_NF4 import OPS
|
||||
from ..modules.bitsandbytes_NF4 import OPS
|
||||
model_options = {"custom_operations": OPS}
|
||||
loaded_ckpt = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=output_clip, output_clipvision=output_clipvision, embedding_directory=folder_paths.get_folder_paths("embeddings"), model_options=model_options)
|
||||
|
||||
@@ -391,7 +391,7 @@ class easyLoader:
|
||||
|
||||
# PixArt
|
||||
if type is not None and type == 'PixArt':
|
||||
from ..dit.pixArt.loader import load_pixart_lora
|
||||
from ..modules.dit.pixArt.loader import load_pixart_lora
|
||||
model = load_pixart_lora(model, _lora, lora_path, model_strength)
|
||||
else:
|
||||
model, clip = comfy.sd.load_lora_for_models(model, clip, _lora, model_strength, clip_strength)
|
||||
@@ -489,7 +489,7 @@ class easyLoader:
|
||||
log_node_info("Load Kolors UNet", f"{unet_name} cached")
|
||||
return self.loaded_objects["unet"][unet_name][0]
|
||||
else:
|
||||
from ..kolors.loader import applyKolorsUnet
|
||||
from ..modules.kolors import applyKolorsUnet
|
||||
with applyKolorsUnet():
|
||||
unet_path = folder_paths.get_full_path("unet", unet_name)
|
||||
sd = comfy.utils.load_torch_file(unet_path)
|
||||
@@ -503,7 +503,7 @@ class easyLoader:
|
||||
return model
|
||||
|
||||
def load_chatglm3(self, chatglm3_name):
|
||||
from ..kolors.loader import load_chatglm3
|
||||
from ..modules.kolors.loader import load_chatglm3
|
||||
if chatglm3_name in self.loaded_objects["chatglm3"]:
|
||||
log_node_info("Load ChatGLM3", f"{chatglm3_name} cached")
|
||||
return self.loaded_objects["chatglm3"][chatglm3_name][0]
|
||||
@@ -531,50 +531,6 @@ class easyLoader:
|
||||
self.eviction_based_on_memory()
|
||||
return model
|
||||
|
||||
|
||||
def load_dit_clip(self, clip_name, **kwargs):
|
||||
if clip_name in self.loaded_objects["clip"]:
|
||||
return self.loaded_objects["clip"][clip_name][0]
|
||||
|
||||
clip_path = folder_paths.get_full_path("clip", clip_name)
|
||||
sd = comfy.utils.load_torch_file(clip_path)
|
||||
|
||||
prefix = "bert."
|
||||
state_dict = {}
|
||||
for key in sd:
|
||||
nkey = key
|
||||
if key.startswith(prefix):
|
||||
nkey = key[len(prefix):]
|
||||
state_dict[nkey] = sd[key]
|
||||
|
||||
m, e = model.load_sd(state_dict)
|
||||
if len(m) > 0 or len(e) > 0:
|
||||
print(f"{clip_name}: clip missing {len(m)} keys ({len(e)} extra)")
|
||||
|
||||
self.add_to_cache("clip", clip_name, model)
|
||||
self.eviction_based_on_memory()
|
||||
|
||||
return model
|
||||
|
||||
def load_dit_t5(self, t5_name, **kwargs):
|
||||
if t5_name in self.loaded_objects["t5"]:
|
||||
return self.loaded_objects["t5"][t5_name][0]
|
||||
|
||||
model_type = kwargs['model_type'] if "model_type" in kwargs else 'HyDiT'
|
||||
if model_type == 'HyDiT':
|
||||
del kwargs['model_type']
|
||||
model = EXM_HyDiT_Tenc_Temp(model_class="mT5", **kwargs)
|
||||
t5_path = folder_paths.get_full_path("t5", t5_name)
|
||||
sd = comfy.utils.load_torch_file(t5_path)
|
||||
m, e = model.load_sd(sd)
|
||||
if len(m) > 0 or len(e) > 0:
|
||||
print(f"{t5_name}: mT5 missing {len(m)} keys ({len(e)} extra)")
|
||||
|
||||
self.add_to_cache("t5", t5_name, model)
|
||||
self.eviction_based_on_memory()
|
||||
|
||||
return model
|
||||
|
||||
def load_t5_from_sd3_clip(self, sd3_clip, padding):
|
||||
try:
|
||||
from comfy.text_encoders.sd3_clip import SD3Tokenizer, SD3ClipModel
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import latent_preview
|
||||
from nodes import MAX_RESOLUTION
|
||||
from PIL import Image
|
||||
from typing import Dict, List, Optional, Tuple, Union, Any
|
||||
from ..brushnet.model_patch import add_model_patch
|
||||
from ..modules.brushnet.model_patch import add_model_patch
|
||||
|
||||
class easySampler:
|
||||
def __init__(self):
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ from .utils import easySave, get_sd_version
|
||||
from .adv_encode import advanced_encode
|
||||
from .controlnet import easyControlnet
|
||||
from .log import log_node_warn
|
||||
from ..layer_diffuse import LayerDiffuse
|
||||
from ..modules.layer_diffuse import LayerDiffuse
|
||||
from ..config import RESOURCES_DIR
|
||||
from nodes import CLIPTextEncode
|
||||
try:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import comfy.ops
|
||||
import torch
|
||||
import folder_paths
|
||||
from ..libs.utils import install_package
|
||||
from ...libs.utils import install_package
|
||||
|
||||
try:
|
||||
from bitsandbytes.nn.modules import Params4bit, QuantState
|
||||
@@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
import torch
|
||||
from torch import nn
|
||||
|
||||
from ..libs.utils import install_package
|
||||
from ...libs.utils import install_package
|
||||
try:
|
||||
install_package("diffusers", "0.27.2", True, "0.25.0")
|
||||
|
||||
@@ -25,7 +25,7 @@ try:
|
||||
from diffusers.models.transformers.dual_transformer_2d import DualTransformer2DModel
|
||||
from diffusers.models.transformers.transformer_2d import Transformer2DModel
|
||||
|
||||
from .unet_2d_blocks import (
|
||||
from py.modules.brushnet.unet_2d_blocks import (
|
||||
CrossAttnDownBlock2D,
|
||||
DownBlock2D,
|
||||
get_down_block,
|
||||
@@ -33,7 +33,7 @@ try:
|
||||
get_up_block,
|
||||
)
|
||||
|
||||
from .unet_2d_condition import UNet2DConditionModel
|
||||
from py.modules.brushnet.unet_2d_condition import UNet2DConditionModel
|
||||
|
||||
logger = logging.get_logger(__name__)
|
||||
|
||||
@@ -43,7 +43,7 @@ from diffusers.models.embeddings import (
|
||||
Timesteps,
|
||||
)
|
||||
from diffusers.models.modeling_utils import ModelMixin
|
||||
from .unet_2d_blocks import (
|
||||
from py.modules.brushnet.unet_2d_blocks import (
|
||||
get_down_block,
|
||||
get_mid_block,
|
||||
get_up_block,
|
||||
@@ -85,23 +85,23 @@ def load_pixart(model_path, model_conf=None):
|
||||
)
|
||||
|
||||
if model_conf.model_target == "PixArtMS":
|
||||
from .models.PixArtMS import PixArtMS
|
||||
from py.modules.dit.pixArt.models.PixArtMS import PixArtMS
|
||||
model.diffusion_model = PixArtMS(**model_conf.unet_config)
|
||||
elif model_conf.model_target == "PixArt":
|
||||
from .models.PixArt import PixArt
|
||||
from py.modules.dit.pixArt.models.PixArt import PixArt
|
||||
model.diffusion_model = PixArt(**model_conf.unet_config)
|
||||
elif model_conf.model_target == "PixArtMSSigma":
|
||||
from .models.PixArtMS import PixArtMS
|
||||
from py.modules.dit.pixArt.models.PixArtMS import PixArtMS
|
||||
model.diffusion_model = PixArtMS(**model_conf.unet_config)
|
||||
model.latent_format = comfy.latent_formats.SDXL()
|
||||
elif model_conf.model_target == "ControlPixArtMSHalf":
|
||||
from .models.PixArtMS import PixArtMS
|
||||
from .models.pixart_controlnet import ControlPixArtMSHalf
|
||||
from py.modules.dit.pixArt.models.PixArtMS import PixArtMS
|
||||
from py.modules.dit.pixArt.models.pixart_controlnet import ControlPixArtMSHalf
|
||||
model.diffusion_model = PixArtMS(**model_conf.unet_config)
|
||||
model.diffusion_model = ControlPixArtMSHalf(model.diffusion_model)
|
||||
elif model_conf.model_target == "ControlPixArtHalf":
|
||||
from .models.PixArt import PixArt
|
||||
from .models.pixart_controlnet import ControlPixArtHalf
|
||||
from py.modules.dit.pixArt.models.PixArt import PixArt
|
||||
from py.modules.dit.pixArt.models.pixart_controlnet import ControlPixArtHalf
|
||||
model.diffusion_model = PixArt(**model_conf.unet_config)
|
||||
model.diffusion_model = ControlPixArtHalf(model.diffusion_model)
|
||||
else:
|
||||
@@ -17,8 +17,8 @@ from timm.models.layers import DropPath
|
||||
from timm.models.vision_transformer import PatchEmbed, Mlp
|
||||
|
||||
|
||||
from .utils import auto_grad_checkpoint, to_2tuple
|
||||
from .PixArt_blocks import t2i_modulate, CaptionEmbedder, AttentionKVCompress, MultiHeadCrossAttention, T2IFinalLayer, TimestepEmbedder, LabelEmbedder, FinalLayer
|
||||
from py.modules.dit.pixArt.models.utils import auto_grad_checkpoint, to_2tuple
|
||||
from py.modules.dit.pixArt.models.PixArt_blocks import t2i_modulate, CaptionEmbedder, AttentionKVCompress, MultiHeadCrossAttention, T2IFinalLayer, TimestepEmbedder, LabelEmbedder, FinalLayer
|
||||
|
||||
|
||||
class PixArtBlock(nn.Module):
|
||||
@@ -14,9 +14,9 @@ from tqdm import tqdm
|
||||
from timm.models.layers import DropPath
|
||||
from timm.models.vision_transformer import Mlp
|
||||
|
||||
from .utils import auto_grad_checkpoint, to_2tuple
|
||||
from .PixArt_blocks import t2i_modulate, CaptionEmbedder, AttentionKVCompress, MultiHeadCrossAttention, T2IFinalLayer, TimestepEmbedder, SizeEmbedder
|
||||
from .PixArt import PixArt, get_2d_sincos_pos_embed
|
||||
from py.modules.dit.pixArt.models.utils import auto_grad_checkpoint, to_2tuple
|
||||
from py.modules.dit.pixArt.models.PixArt_blocks import t2i_modulate, CaptionEmbedder, AttentionKVCompress, MultiHeadCrossAttention, T2IFinalLayer, TimestepEmbedder, SizeEmbedder
|
||||
from py.modules.dit.pixArt.models.PixArt import PixArt, get_2d_sincos_pos_embed
|
||||
|
||||
|
||||
class PatchEmbed(nn.Module):
|
||||
+3
-3
@@ -7,9 +7,9 @@ from torch import Tensor
|
||||
from torch.nn import Module, Linear, init
|
||||
from typing import Any, Mapping
|
||||
|
||||
from .PixArt import PixArt, get_2d_sincos_pos_embed
|
||||
from .PixArtMS import PixArtMSBlock, PixArtMS
|
||||
from .utils import auto_grad_checkpoint
|
||||
from py.modules.dit.pixArt.models.PixArt import PixArt, get_2d_sincos_pos_embed
|
||||
from py.modules.dit.pixArt.models.PixArtMS import PixArtMSBlock, PixArtMS
|
||||
from py.modules.dit.pixArt.models.utils import auto_grad_checkpoint
|
||||
|
||||
# The implementation of ControlNet-Half architrecture
|
||||
# https://github.com/lllyasviel/ControlNet/discussions/188
|
||||
@@ -7,7 +7,7 @@ from comfy.model_base import BaseModel
|
||||
from comfy.model_patcher import ModelPatcher
|
||||
from comfy.model_management import cast_to_device
|
||||
|
||||
from ..libs.log import log_node_warn, log_node_error, log_node_info
|
||||
from ...libs.log import log_node_warn, log_node_error, log_node_info
|
||||
|
||||
class InpaintHead(torch.nn.Module):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -4,7 +4,7 @@ import cv2
|
||||
import torchvision.transforms as transforms
|
||||
from torch.utils.data import DataLoader
|
||||
from .simple_extractor_dataset import SimpleFolderDataset
|
||||
from .transforms import transform_logits
|
||||
from transforms import transform_logits
|
||||
from tqdm import tqdm
|
||||
from PIL import Image
|
||||
|
||||
@@ -2,7 +2,7 @@ import numpy as np
|
||||
import torch
|
||||
from PIL import Image
|
||||
from .parsing_api import onnx_inference
|
||||
from ..libs.utils import install_package
|
||||
from ...libs.utils import install_package
|
||||
|
||||
class HumanParsing:
|
||||
def __init__(self, model_path):
|
||||
@@ -11,7 +11,7 @@ from comfy.model_base import BaseModel
|
||||
from comfy.model_patcher import ModelPatcher
|
||||
from PIL import Image
|
||||
from nodes import VAEEncode
|
||||
from ..libs.image import np2tensor, pil2tensor
|
||||
from ...libs.image import np2tensor, pil2tensor
|
||||
|
||||
class UnetParams(TypedDict):
|
||||
input: torch.Tensor
|
||||
+1
-1
@@ -29,7 +29,7 @@ from transformers.generation.utils import LogitsProcessorList, StoppingCriteriaL
|
||||
try:
|
||||
from .configuration_chatglm import ChatGLMConfig
|
||||
except:
|
||||
from configuration_chatglm import ChatGLMConfig
|
||||
from .configuration_chatglm import ChatGLMConfig
|
||||
|
||||
|
||||
# flags required to enable jit fusion kernels
|
||||
@@ -11,8 +11,8 @@ from comfy.conds import CONDRegular
|
||||
from comfy_extras.nodes_compositing import JoinImageWithAlpha
|
||||
from .model import ModelPatcher, TransparentVAEDecoder, calculate_weight_adjust_channel
|
||||
from .attension_sharing import AttentionSharingPatcher
|
||||
from ..config import LAYER_DIFFUSION, LAYER_DIFFUSION_DIR, LAYER_DIFFUSION_VAE
|
||||
from ..libs.utils import to_lora_patch_dict, get_local_filepath, get_sd_version
|
||||
from ...config import LAYER_DIFFUSION, LAYER_DIFFUSION_DIR, LAYER_DIFFUSION_VAE
|
||||
from ...libs.utils import to_lora_patch_dict, get_local_filepath, get_sd_version
|
||||
|
||||
load_layer_model_state_dict = load_torch_file
|
||||
class LayerMethod(Enum):
|
||||
@@ -7,7 +7,7 @@ import comfy.model_management
|
||||
from comfy.model_patcher import ModelPatcher
|
||||
from tqdm import tqdm
|
||||
from typing import Optional, Tuple
|
||||
from ..libs.utils import install_package
|
||||
from ...libs.utils import install_package
|
||||
from packaging import version
|
||||
|
||||
try:
|
||||
+1322
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,79 @@
|
||||
from ..libs.stability import stableAPI
|
||||
class stableDiffusion3API:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"positive": ("STRING", {"default": "", "placeholder": "Positive", "multiline": True}),
|
||||
"negative": ("STRING", {"default": "", "placeholder": "Negative", "multiline": True}),
|
||||
"model": (["sd3", "sd3-turbo"],),
|
||||
"aspect_ratio": (['16:9', '1:1', '21:9', '2:3', '3:2', '4:5', '5:4', '9:16', '9:21'],),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 4294967294}),
|
||||
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0}),
|
||||
},
|
||||
"optional": {
|
||||
"optional_image": ("IMAGE",),
|
||||
},
|
||||
"hidden": {
|
||||
"unique_id": "UNIQUE_ID",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image",)
|
||||
|
||||
FUNCTION = "generate"
|
||||
OUTPUT_NODE = False
|
||||
|
||||
CATEGORY = "EasyUse/API"
|
||||
|
||||
def generate(self, positive, negative, model, aspect_ratio, seed, denoise, optional_image=None, unique_id=None, extra_pnginfo=None):
|
||||
mode = 'text-to-image'
|
||||
if optional_image is not None:
|
||||
mode = 'image-to-image'
|
||||
output_image = stableAPI.generate_sd3_image(positive, negative, aspect_ratio, seed=seed, mode=mode, model=model, strength=denoise, image=optional_image)
|
||||
return (output_image,)
|
||||
|
||||
from ..libs.fluxai import fluxaiAPI
|
||||
|
||||
class fluxPromptGenAPI:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"describe": ("STRING", {"default": "", "placeholder": "Describe your image idea (you can use any language)", "multiline": True}),
|
||||
},
|
||||
"optional": {
|
||||
"cookie_override": ("STRING", {"default": "", "forceInput": True}),
|
||||
},
|
||||
"hidden": {
|
||||
"prompt": "PROMPT",
|
||||
"unique_id": "UNIQUE_ID",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompt",)
|
||||
|
||||
FUNCTION = "generate"
|
||||
OUTPUT_NODE = False
|
||||
|
||||
CATEGORY = "EasyUse/API"
|
||||
|
||||
def generate(self, describe, cookie_override=None, prompt=None, unique_id=None, extra_pnginfo=None):
|
||||
prompt = fluxaiAPI.promptGenerate(describe, cookie_override)
|
||||
return (prompt,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"easy stableDiffusion3API": stableDiffusion3API,
|
||||
"easy fluxPromptGenAPI": fluxPromptGenAPI,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy stableDiffusion3API": "Stable Diffusion 3 (API)",
|
||||
"easy fluxPromptGenAPI": "Flux Prompt Gen (API)",
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import torch
|
||||
import comfy
|
||||
import comfy.model_management
|
||||
from .libs.log import log_node_info, log_node_warn
|
||||
from .libs.adv_encode import advanced_encode
|
||||
from nodes import ConditioningSetMask, RepeatLatentBatch
|
||||
from comfy_extras.nodes_mask import LatentCompositeMasked
|
||||
from .libs.utils import AlwaysEqualProxy
|
||||
from ..libs.log import log_node_info, log_node_warn
|
||||
from ..libs.adv_encode import advanced_encode
|
||||
from ..libs.utils import AlwaysEqualProxy
|
||||
any_type = AlwaysEqualProxy("*")
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class imageToMask:
|
||||
return channel_img
|
||||
|
||||
def convert(self, image, channel='red'):
|
||||
from .libs.image import pil2tensor, tensor2pil
|
||||
from ..libs.image import pil2tensor, tensor2pil
|
||||
image = self.convert_to_single_channel(tensor2pil(image), channel)
|
||||
image = pil2tensor(image)
|
||||
return (image.squeeze().mean(2),)
|
||||
+643
@@ -0,0 +1,643 @@
|
||||
import sys
|
||||
import time
|
||||
import comfy
|
||||
import torch
|
||||
import folder_paths
|
||||
|
||||
from comfy_extras.chainner_models import model_loading
|
||||
|
||||
from server import PromptServer
|
||||
from nodes import MAX_RESOLUTION, NODE_CLASS_MAPPINGS as ALL_NODE_CLASS_MAPPINGS
|
||||
|
||||
from ..libs.utils import easySave, get_sd_version
|
||||
from ..libs.sampler import easySampler
|
||||
from .. import easyCache, sampler
|
||||
|
||||
class hiresFix:
|
||||
upscale_methods = ["nearest-exact", "bilinear", "area", "bicubic", "lanczos", "bislerp"]
|
||||
crop_methods = ["disabled", "center"]
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"model_name": (folder_paths.get_filename_list("upscale_models"),),
|
||||
"rescale_after_model": ([False, True], {"default": True}),
|
||||
"rescale_method": (s.upscale_methods,),
|
||||
"rescale": (["by percentage", "to Width/Height", 'to longer side - maintain aspect'],),
|
||||
"percent": ("INT", {"default": 50, "min": 0, "max": 1000, "step": 1}),
|
||||
"width": ("INT", {"default": 1024, "min": 64, "max": MAX_RESOLUTION, "step": 8}),
|
||||
"height": ("INT", {"default": 1024, "min": 64, "max": MAX_RESOLUTION, "step": 8}),
|
||||
"longer_side": ("INT", {"default": 1024, "min": 64, "max": MAX_RESOLUTION, "step": 8}),
|
||||
"crop": (s.crop_methods,),
|
||||
"image_output": (["Hide", "Preview", "Save", "Hide&Save", "Sender", "Sender&Save"],{"default": "Preview"}),
|
||||
"link_id": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1}),
|
||||
"save_prefix": ("STRING", {"default": "ComfyUI"}),
|
||||
},
|
||||
"optional": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"image": ("IMAGE",),
|
||||
"vae": ("VAE",),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID",
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE", "IMAGE", "LATENT", )
|
||||
RETURN_NAMES = ('pipe', 'image', "latent", )
|
||||
|
||||
FUNCTION = "upscale"
|
||||
CATEGORY = "EasyUse/Fix"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def vae_encode_crop_pixels(self, pixels):
|
||||
x = (pixels.shape[1] // 8) * 8
|
||||
y = (pixels.shape[2] // 8) * 8
|
||||
if pixels.shape[1] != x or pixels.shape[2] != y:
|
||||
x_offset = (pixels.shape[1] % 8) // 2
|
||||
y_offset = (pixels.shape[2] % 8) // 2
|
||||
pixels = pixels[:, x_offset:x + x_offset, y_offset:y + y_offset, :]
|
||||
return pixels
|
||||
|
||||
def upscale(self, model_name, rescale_after_model, rescale_method, rescale, percent, width, height,
|
||||
longer_side, crop, image_output, link_id, save_prefix, pipe=None, image=None, vae=None, prompt=None,
|
||||
extra_pnginfo=None, my_unique_id=None):
|
||||
|
||||
new_pipe = {}
|
||||
if pipe is not None:
|
||||
image = image if image is not None else pipe["images"]
|
||||
vae = vae if vae is not None else pipe.get("vae")
|
||||
elif image is None or vae is None:
|
||||
raise ValueError("pipe or image or vae missing.")
|
||||
# Load Model
|
||||
model_path = folder_paths.get_full_path("upscale_models", model_name)
|
||||
sd = comfy.utils.load_torch_file(model_path, safe_load=True)
|
||||
upscale_model = model_loading.load_state_dict(sd).eval()
|
||||
|
||||
# Model upscale
|
||||
device = comfy.model_management.get_torch_device()
|
||||
upscale_model.to(device)
|
||||
in_img = image.movedim(-1, -3).to(device)
|
||||
|
||||
tile = 128 + 64
|
||||
overlap = 8
|
||||
steps = in_img.shape[0] * comfy.utils.get_tiled_scale_steps(in_img.shape[3], in_img.shape[2], tile_x=tile,
|
||||
tile_y=tile, overlap=overlap)
|
||||
pbar = comfy.utils.ProgressBar(steps)
|
||||
s = comfy.utils.tiled_scale(in_img, lambda a: upscale_model(a), tile_x=tile, tile_y=tile, overlap=overlap,
|
||||
upscale_amount=upscale_model.scale, pbar=pbar)
|
||||
upscale_model.cpu()
|
||||
s = torch.clamp(s.movedim(-3, -1), min=0, max=1.0)
|
||||
|
||||
# Post Model Rescale
|
||||
if rescale_after_model == True:
|
||||
samples = s.movedim(-1, 1)
|
||||
orig_height = samples.shape[2]
|
||||
orig_width = samples.shape[3]
|
||||
if rescale == "by percentage" and percent != 0:
|
||||
height = percent / 100 * orig_height
|
||||
width = percent / 100 * orig_width
|
||||
if (width > MAX_RESOLUTION):
|
||||
width = MAX_RESOLUTION
|
||||
if (height > MAX_RESOLUTION):
|
||||
height = MAX_RESOLUTION
|
||||
|
||||
width = easySampler.enforce_mul_of_64(width)
|
||||
height = easySampler.enforce_mul_of_64(height)
|
||||
elif rescale == "to longer side - maintain aspect":
|
||||
longer_side = easySampler.enforce_mul_of_64(longer_side)
|
||||
if orig_width > orig_height:
|
||||
width, height = longer_side, easySampler.enforce_mul_of_64(longer_side * orig_height / orig_width)
|
||||
else:
|
||||
width, height = easySampler.enforce_mul_of_64(longer_side * orig_width / orig_height), longer_side
|
||||
|
||||
s = comfy.utils.common_upscale(samples, width, height, rescale_method, crop)
|
||||
s = s.movedim(1, -1)
|
||||
|
||||
# vae encode
|
||||
pixels = self.vae_encode_crop_pixels(s)
|
||||
t = vae.encode(pixels[:, :, :, :3])
|
||||
|
||||
if pipe is not None:
|
||||
new_pipe = {
|
||||
"model": pipe['model'],
|
||||
"positive": pipe['positive'],
|
||||
"negative": pipe['negative'],
|
||||
"vae": vae,
|
||||
"clip": pipe['clip'],
|
||||
|
||||
"samples": {"samples": t},
|
||||
"images": s,
|
||||
"seed": pipe['seed'],
|
||||
|
||||
"loader_settings": {
|
||||
**pipe["loader_settings"],
|
||||
}
|
||||
}
|
||||
del pipe
|
||||
else:
|
||||
new_pipe = {}
|
||||
|
||||
results = easySave(s, save_prefix, image_output, prompt, extra_pnginfo)
|
||||
|
||||
if image_output in ("Sender", "Sender&Save"):
|
||||
PromptServer.instance.send_sync("img-send", {"link_id": link_id, "images": results})
|
||||
|
||||
if image_output in ("Hide", "Hide&Save"):
|
||||
return (new_pipe, s, {"samples": t},)
|
||||
|
||||
return {"ui": {"images": results},
|
||||
"result": (new_pipe, s, {"samples": t},)}
|
||||
|
||||
# 预细节修复
|
||||
class preDetailerFix:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"guide_size": ("FLOAT", {"default": 256, "min": 64, "max": MAX_RESOLUTION, "step": 8}),
|
||||
"guide_size_for": ("BOOLEAN", {"default": True, "label_on": "bbox", "label_off": "crop_region"}),
|
||||
"max_size": ("FLOAT", {"default": 768, "min": 64, "max": MAX_RESOLUTION, "step": 8}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
||||
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}),
|
||||
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
|
||||
"scheduler": (comfy.samplers.KSampler.SCHEDULERS + ['align_your_steps'],),
|
||||
"denoise": ("FLOAT", {"default": 0.5, "min": 0.0001, "max": 1.0, "step": 0.01}),
|
||||
"feather": ("INT", {"default": 5, "min": 0, "max": 100, "step": 1}),
|
||||
"noise_mask": ("BOOLEAN", {"default": True, "label_on": "enabled", "label_off": "disabled"}),
|
||||
"force_inpaint": ("BOOLEAN", {"default": True, "label_on": "enabled", "label_off": "disabled"}),
|
||||
"drop_size": ("INT", {"min": 1, "max": MAX_RESOLUTION, "step": 1, "default": 10}),
|
||||
"wildcard": ("STRING", {"multiline": True, "dynamicPrompts": False}),
|
||||
"cycle": ("INT", {"default": 1, "min": 1, "max": 10, "step": 1}),
|
||||
},
|
||||
"optional": {
|
||||
"bbox_segm_pipe": ("PIPE_LINE",),
|
||||
"sam_pipe": ("PIPE_LINE",),
|
||||
"optional_image": ("IMAGE",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("pipe",)
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "EasyUse/Fix"
|
||||
|
||||
def doit(self, pipe, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler, denoise, feather, noise_mask, force_inpaint, drop_size, wildcard, cycle, bbox_segm_pipe=None, sam_pipe=None, optional_image=None):
|
||||
|
||||
model = pipe["model"] if "model" in pipe else None
|
||||
if model is None:
|
||||
raise Exception(f"[ERROR] pipe['model'] is missing")
|
||||
clip = pipe["clip"] if"clip" in pipe else None
|
||||
if clip is None:
|
||||
raise Exception(f"[ERROR] pipe['clip'] is missing")
|
||||
vae = pipe["vae"] if "vae" in pipe else None
|
||||
if vae is None:
|
||||
raise Exception(f"[ERROR] pipe['vae'] is missing")
|
||||
if optional_image is not None:
|
||||
images = optional_image
|
||||
else:
|
||||
images = pipe["images"] if "images" in pipe else None
|
||||
if images is None:
|
||||
raise Exception(f"[ERROR] pipe['image'] is missing")
|
||||
positive = pipe["positive"] if "positive" in pipe else None
|
||||
if positive is None:
|
||||
raise Exception(f"[ERROR] pipe['positive'] is missing")
|
||||
negative = pipe["negative"] if "negative" in pipe else None
|
||||
if negative is None:
|
||||
raise Exception(f"[ERROR] pipe['negative'] is missing")
|
||||
bbox_segm_pipe = bbox_segm_pipe or (pipe["bbox_segm_pipe"] if pipe and "bbox_segm_pipe" in pipe else None)
|
||||
if bbox_segm_pipe is None:
|
||||
raise Exception(f"[ERROR] bbox_segm_pipe or pipe['bbox_segm_pipe'] is missing")
|
||||
sam_pipe = sam_pipe or (pipe["sam_pipe"] if pipe and "sam_pipe" in pipe else None)
|
||||
if sam_pipe is None:
|
||||
raise Exception(f"[ERROR] sam_pipe or pipe['sam_pipe'] is missing")
|
||||
|
||||
loader_settings = pipe["loader_settings"] if "loader_settings" in pipe else {}
|
||||
|
||||
if(scheduler == 'align_your_steps'):
|
||||
model_version = get_sd_version(model)
|
||||
if model_version == 'sdxl':
|
||||
scheduler = 'AYS SDXL'
|
||||
elif model_version == 'svd':
|
||||
scheduler = 'AYS SVD'
|
||||
else:
|
||||
scheduler = 'AYS SD1'
|
||||
|
||||
new_pipe = {
|
||||
"images": images,
|
||||
"model": model,
|
||||
"clip": clip,
|
||||
"vae": vae,
|
||||
"positive": positive,
|
||||
"negative": negative,
|
||||
"seed": seed,
|
||||
|
||||
"bbox_segm_pipe": bbox_segm_pipe,
|
||||
"sam_pipe": sam_pipe,
|
||||
|
||||
"loader_settings": loader_settings,
|
||||
|
||||
"detail_fix_settings": {
|
||||
"guide_size": guide_size,
|
||||
"guide_size_for": guide_size_for,
|
||||
"max_size": max_size,
|
||||
"seed": seed,
|
||||
"steps": steps,
|
||||
"cfg": cfg,
|
||||
"sampler_name": sampler_name,
|
||||
"scheduler": scheduler,
|
||||
"denoise": denoise,
|
||||
"feather": feather,
|
||||
"noise_mask": noise_mask,
|
||||
"force_inpaint": force_inpaint,
|
||||
"drop_size": drop_size,
|
||||
"wildcard": wildcard,
|
||||
"cycle": cycle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
del bbox_segm_pipe
|
||||
del sam_pipe
|
||||
|
||||
return (new_pipe,)
|
||||
|
||||
# 预遮罩细节修复
|
||||
class preMaskDetailerFix:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"mask": ("MASK",),
|
||||
|
||||
"guide_size": ("FLOAT", {"default": 384, "min": 64, "max": MAX_RESOLUTION, "step": 8}),
|
||||
"guide_size_for": ("BOOLEAN", {"default": True, "label_on": "bbox", "label_off": "crop_region"}),
|
||||
"max_size": ("FLOAT", {"default": 1024, "min": 64, "max": MAX_RESOLUTION, "step": 8}),
|
||||
"mask_mode": ("BOOLEAN", {"default": True, "label_on": "masked only", "label_off": "whole"}),
|
||||
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
||||
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}),
|
||||
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
|
||||
"scheduler": (comfy.samplers.KSampler.SCHEDULERS,),
|
||||
"denoise": ("FLOAT", {"default": 0.5, "min": 0.0001, "max": 1.0, "step": 0.01}),
|
||||
|
||||
"feather": ("INT", {"default": 5, "min": 0, "max": 100, "step": 1}),
|
||||
"crop_factor": ("FLOAT", {"default": 3.0, "min": 1.0, "max": 10, "step": 0.1}),
|
||||
"drop_size": ("INT", {"min": 1, "max": MAX_RESOLUTION, "step": 1, "default": 10}),
|
||||
"refiner_ratio": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0}),
|
||||
"batch_size": ("INT", {"default": 1, "min": 1, "max": 100}),
|
||||
"cycle": ("INT", {"default": 1, "min": 1, "max": 10, "step": 1}),
|
||||
},
|
||||
"optional": {
|
||||
# "patch": ("INPAINT_PATCH",),
|
||||
"optional_image": ("IMAGE",),
|
||||
"inpaint_model": ("BOOLEAN", {"default": False, "label_on": "enabled", "label_off": "disabled"}),
|
||||
"noise_mask_feather": ("INT", {"default": 20, "min": 0, "max": 100, "step": 1}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("pipe",)
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "EasyUse/Fix"
|
||||
|
||||
def doit(self, pipe, mask, guide_size, guide_size_for, max_size, mask_mode, seed, steps, cfg, sampler_name, scheduler, denoise, feather, crop_factor, drop_size,refiner_ratio, batch_size, cycle, optional_image=None, inpaint_model=False, noise_mask_feather=20):
|
||||
|
||||
model = pipe["model"] if "model" in pipe else None
|
||||
if model is None:
|
||||
raise Exception(f"[ERROR] pipe['model'] is missing")
|
||||
clip = pipe["clip"] if"clip" in pipe else None
|
||||
if clip is None:
|
||||
raise Exception(f"[ERROR] pipe['clip'] is missing")
|
||||
vae = pipe["vae"] if "vae" in pipe else None
|
||||
if vae is None:
|
||||
raise Exception(f"[ERROR] pipe['vae'] is missing")
|
||||
if optional_image is not None:
|
||||
images = optional_image
|
||||
else:
|
||||
images = pipe["images"] if "images" in pipe else None
|
||||
if images is None:
|
||||
raise Exception(f"[ERROR] pipe['image'] is missing")
|
||||
positive = pipe["positive"] if "positive" in pipe else None
|
||||
if positive is None:
|
||||
raise Exception(f"[ERROR] pipe['positive'] is missing")
|
||||
negative = pipe["negative"] if "negative" in pipe else None
|
||||
if negative is None:
|
||||
raise Exception(f"[ERROR] pipe['negative'] is missing")
|
||||
latent = pipe["samples"] if "samples" in pipe else None
|
||||
if latent is None:
|
||||
raise Exception(f"[ERROR] pipe['samples'] is missing")
|
||||
|
||||
if 'noise_mask' not in latent:
|
||||
if images is None:
|
||||
raise Exception("No Images found")
|
||||
if vae is None:
|
||||
raise Exception("No VAE found")
|
||||
x = (images.shape[1] // 8) * 8
|
||||
y = (images.shape[2] // 8) * 8
|
||||
mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])),
|
||||
size=(images.shape[1], images.shape[2]), mode="bilinear")
|
||||
|
||||
pixels = images.clone()
|
||||
if pixels.shape[1] != x or pixels.shape[2] != y:
|
||||
x_offset = (pixels.shape[1] % 8) // 2
|
||||
y_offset = (pixels.shape[2] % 8) // 2
|
||||
pixels = pixels[:, x_offset:x + x_offset, y_offset:y + y_offset, :]
|
||||
mask = mask[:, :, x_offset:x + x_offset, y_offset:y + y_offset]
|
||||
|
||||
mask_erosion = mask
|
||||
|
||||
m = (1.0 - mask.round()).squeeze(1)
|
||||
for i in range(3):
|
||||
pixels[:, :, :, i] -= 0.5
|
||||
pixels[:, :, :, i] *= m
|
||||
pixels[:, :, :, i] += 0.5
|
||||
t = vae.encode(pixels)
|
||||
|
||||
latent = {"samples": t, "noise_mask": (mask_erosion[:, :, :x, :y].round())}
|
||||
# when patch was linked
|
||||
# if patch is not None:
|
||||
# worker = InpaintWorker(node_name="easy kSamplerInpainting")
|
||||
# model, = worker.patch(model, latent, patch)
|
||||
|
||||
loader_settings = pipe["loader_settings"] if "loader_settings" in pipe else {}
|
||||
|
||||
new_pipe = {
|
||||
"images": images,
|
||||
"model": model,
|
||||
"clip": clip,
|
||||
"vae": vae,
|
||||
"positive": positive,
|
||||
"negative": negative,
|
||||
"seed": seed,
|
||||
"mask": mask,
|
||||
|
||||
"loader_settings": loader_settings,
|
||||
|
||||
"detail_fix_settings": {
|
||||
"guide_size": guide_size,
|
||||
"guide_size_for": guide_size_for,
|
||||
"max_size": max_size,
|
||||
"seed": seed,
|
||||
"steps": steps,
|
||||
"cfg": cfg,
|
||||
"sampler_name": sampler_name,
|
||||
"scheduler": scheduler,
|
||||
"denoise": denoise,
|
||||
"feather": feather,
|
||||
"crop_factor": crop_factor,
|
||||
"drop_size": drop_size,
|
||||
"refiner_ratio": refiner_ratio,
|
||||
"batch_size": batch_size,
|
||||
"cycle": cycle
|
||||
},
|
||||
|
||||
"mask_settings": {
|
||||
"mask_mode": mask_mode,
|
||||
"inpaint_model": inpaint_model,
|
||||
"noise_mask_feather": noise_mask_feather
|
||||
}
|
||||
}
|
||||
|
||||
del pipe
|
||||
|
||||
return (new_pipe,)
|
||||
|
||||
# 细节修复
|
||||
class detailerFix:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"image_output": (["Hide", "Preview", "Save", "Hide&Save", "Sender", "Sender&Save"],{"default": "Preview"}),
|
||||
"link_id": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1}),
|
||||
"save_prefix": ("STRING", {"default": "ComfyUI"}),
|
||||
},
|
||||
"optional": {
|
||||
"model": ("MODEL",),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID", }
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE", "IMAGE", "IMAGE", "IMAGE")
|
||||
RETURN_NAMES = ("pipe", "image", "cropped_refined", "cropped_enhanced_alpha")
|
||||
OUTPUT_NODE = True
|
||||
OUTPUT_IS_LIST = (False, False, True, True)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "EasyUse/Fix"
|
||||
|
||||
|
||||
def doit(self, pipe, image_output, link_id, save_prefix, model=None, prompt=None, extra_pnginfo=None, my_unique_id=None):
|
||||
|
||||
# Clean loaded_objects
|
||||
easyCache.update_loaded_objects(prompt)
|
||||
|
||||
my_unique_id = int(my_unique_id)
|
||||
|
||||
model = model or (pipe["model"] if "model" in pipe else None)
|
||||
if model is None:
|
||||
raise Exception(f"[ERROR] model or pipe['model'] is missing")
|
||||
|
||||
detail_fix_settings = pipe["detail_fix_settings"] if "detail_fix_settings" in pipe else None
|
||||
if detail_fix_settings is None:
|
||||
raise Exception(f"[ERROR] detail_fix_settings or pipe['detail_fix_settings'] is missing")
|
||||
|
||||
mask = pipe["mask"] if "mask" in pipe else None
|
||||
|
||||
image = pipe["images"]
|
||||
clip = pipe["clip"]
|
||||
vae = pipe["vae"]
|
||||
seed = pipe["seed"]
|
||||
positive = pipe["positive"]
|
||||
negative = pipe["negative"]
|
||||
loader_settings = pipe["loader_settings"] if "loader_settings" in pipe else {}
|
||||
guide_size = pipe["detail_fix_settings"]["guide_size"] if "guide_size" in pipe["detail_fix_settings"] else 256
|
||||
guide_size_for = pipe["detail_fix_settings"]["guide_size_for"] if "guide_size_for" in pipe[
|
||||
"detail_fix_settings"] else True
|
||||
max_size = pipe["detail_fix_settings"]["max_size"] if "max_size" in pipe["detail_fix_settings"] else 768
|
||||
steps = pipe["detail_fix_settings"]["steps"] if "steps" in pipe["detail_fix_settings"] else 20
|
||||
cfg = pipe["detail_fix_settings"]["cfg"] if "cfg" in pipe["detail_fix_settings"] else 1.0
|
||||
sampler_name = pipe["detail_fix_settings"]["sampler_name"] if "sampler_name" in pipe[
|
||||
"detail_fix_settings"] else None
|
||||
scheduler = pipe["detail_fix_settings"]["scheduler"] if "scheduler" in pipe["detail_fix_settings"] else None
|
||||
denoise = pipe["detail_fix_settings"]["denoise"] if "denoise" in pipe["detail_fix_settings"] else 0.5
|
||||
feather = pipe["detail_fix_settings"]["feather"] if "feather" in pipe["detail_fix_settings"] else 5
|
||||
crop_factor = pipe["detail_fix_settings"]["crop_factor"] if "crop_factor" in pipe["detail_fix_settings"] else 3.0
|
||||
drop_size = pipe["detail_fix_settings"]["drop_size"] if "drop_size" in pipe["detail_fix_settings"] else 10
|
||||
refiner_ratio = pipe["detail_fix_settings"]["refiner_ratio"] if "refiner_ratio" in pipe else 0.2
|
||||
batch_size = pipe["detail_fix_settings"]["batch_size"] if "batch_size" in pipe["detail_fix_settings"] else 1
|
||||
noise_mask = pipe["detail_fix_settings"]["noise_mask"] if "noise_mask" in pipe["detail_fix_settings"] else None
|
||||
force_inpaint = pipe["detail_fix_settings"]["force_inpaint"] if "force_inpaint" in pipe["detail_fix_settings"] else False
|
||||
wildcard = pipe["detail_fix_settings"]["wildcard"] if "wildcard" in pipe["detail_fix_settings"] else ""
|
||||
cycle = pipe["detail_fix_settings"]["cycle"] if "cycle" in pipe["detail_fix_settings"] else 1
|
||||
|
||||
bbox_segm_pipe = pipe["bbox_segm_pipe"] if pipe and "bbox_segm_pipe" in pipe else None
|
||||
sam_pipe = pipe["sam_pipe"] if "sam_pipe" in pipe else None
|
||||
|
||||
# 细节修复初始时间
|
||||
start_time = int(time.time() * 1000)
|
||||
if "mask_settings" in pipe:
|
||||
mask_mode = pipe['mask_settings']["mask_mode"] if "inpaint_model" in pipe['mask_settings'] else True
|
||||
inpaint_model = pipe['mask_settings']["inpaint_model"] if "inpaint_model" in pipe['mask_settings'] else False
|
||||
noise_mask_feather = pipe['mask_settings']["noise_mask_feather"] if "noise_mask_feather" in pipe['mask_settings'] else 20
|
||||
cls = ALL_NODE_CLASS_MAPPINGS["MaskDetailerPipe"]
|
||||
if "MaskDetailerPipe" not in ALL_NODE_CLASS_MAPPINGS:
|
||||
raise Exception(f"[ERROR] To use MaskDetailerPipe, you need to install 'Impact Pack'")
|
||||
basic_pipe = (model, clip, vae, positive, negative)
|
||||
result_img, result_cropped_enhanced, result_cropped_enhanced_alpha, basic_pipe, refiner_basic_pipe_opt = cls().doit(image, mask, basic_pipe, guide_size, guide_size_for, max_size, mask_mode,
|
||||
seed, steps, cfg, sampler_name, scheduler, denoise,
|
||||
feather, crop_factor, drop_size, refiner_ratio, batch_size, cycle=1,
|
||||
refiner_basic_pipe_opt=None, detailer_hook=None, inpaint_model=inpaint_model, noise_mask_feather=noise_mask_feather)
|
||||
result_mask = mask
|
||||
result_cnet_images = ()
|
||||
else:
|
||||
if bbox_segm_pipe is None:
|
||||
raise Exception(f"[ERROR] bbox_segm_pipe or pipe['bbox_segm_pipe'] is missing")
|
||||
if sam_pipe is None:
|
||||
raise Exception(f"[ERROR] sam_pipe or pipe['sam_pipe'] is missing")
|
||||
bbox_detector_opt, bbox_threshold, bbox_dilation, bbox_crop_factor, segm_detector_opt = bbox_segm_pipe
|
||||
sam_model_opt, sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold, sam_mask_hint_use_negative = sam_pipe
|
||||
if "FaceDetailer" not in ALL_NODE_CLASS_MAPPINGS:
|
||||
raise Exception(f"[ERROR] To use FaceDetailer, you need to install 'Impact Pack'")
|
||||
cls = ALL_NODE_CLASS_MAPPINGS["FaceDetailer"]
|
||||
|
||||
result_img, result_cropped_enhanced, result_cropped_enhanced_alpha, result_mask, pipe, result_cnet_images = cls().doit(
|
||||
image, model, clip, vae, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name,
|
||||
scheduler,
|
||||
positive, negative, denoise, feather, noise_mask, force_inpaint,
|
||||
bbox_threshold, bbox_dilation, bbox_crop_factor,
|
||||
sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold,
|
||||
sam_mask_hint_use_negative, drop_size, bbox_detector_opt, wildcard, cycle, sam_model_opt,
|
||||
segm_detector_opt,
|
||||
detailer_hook=None)
|
||||
|
||||
# 细节修复结束时间
|
||||
end_time = int(time.time() * 1000)
|
||||
|
||||
spent_time = 'Fix:' + str((end_time - start_time) / 1000) + '"'
|
||||
|
||||
results = easySave(result_img, save_prefix, image_output, prompt, extra_pnginfo)
|
||||
sampler.update_value_by_id("results", my_unique_id, results)
|
||||
|
||||
# Clean loaded_objects
|
||||
easyCache.update_loaded_objects(prompt)
|
||||
|
||||
new_pipe = {
|
||||
"samples": None,
|
||||
"images": result_img,
|
||||
"model": model,
|
||||
"clip": clip,
|
||||
"vae": vae,
|
||||
"seed": seed,
|
||||
"positive": positive,
|
||||
"negative": negative,
|
||||
"wildcard": wildcard,
|
||||
"bbox_segm_pipe": bbox_segm_pipe,
|
||||
"sam_pipe": sam_pipe,
|
||||
|
||||
"loader_settings": {
|
||||
**loader_settings,
|
||||
"spent_time": spent_time
|
||||
},
|
||||
"detail_fix_settings": detail_fix_settings
|
||||
}
|
||||
if "mask_settings" in pipe:
|
||||
new_pipe["mask_settings"] = pipe["mask_settings"]
|
||||
|
||||
sampler.update_value_by_id("pipe_line", my_unique_id, new_pipe)
|
||||
|
||||
del bbox_segm_pipe
|
||||
del sam_pipe
|
||||
del pipe
|
||||
|
||||
if image_output in ("Hide", "Hide&Save"):
|
||||
return (new_pipe, result_img, result_cropped_enhanced, result_cropped_enhanced_alpha, result_mask, result_cnet_images)
|
||||
|
||||
if image_output in ("Sender", "Sender&Save"):
|
||||
PromptServer.instance.send_sync("img-send", {"link_id": link_id, "images": results})
|
||||
|
||||
return {"ui": {"images": results}, "result": (new_pipe, result_img, result_cropped_enhanced, result_cropped_enhanced_alpha, result_mask, result_cnet_images )}
|
||||
|
||||
class ultralyticsDetectorForDetailerFix:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
bboxs = ["bbox/" + x for x in folder_paths.get_filename_list("ultralytics_bbox")]
|
||||
segms = ["segm/" + x for x in folder_paths.get_filename_list("ultralytics_segm")]
|
||||
return {"required":
|
||||
{"model_name": (bboxs + segms,),
|
||||
"bbox_threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"bbox_dilation": ("INT", {"default": 10, "min": -512, "max": 512, "step": 1}),
|
||||
"bbox_crop_factor": ("FLOAT", {"default": 3.0, "min": 1.0, "max": 10, "step": 0.1}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("bbox_segm_pipe",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "EasyUse/Fix"
|
||||
|
||||
def doit(self, model_name, bbox_threshold, bbox_dilation, bbox_crop_factor):
|
||||
if 'UltralyticsDetectorProvider' not in ALL_NODE_CLASS_MAPPINGS:
|
||||
raise Exception(f"[ERROR] To use UltralyticsDetectorProvider, you need to install 'Impact Pack'")
|
||||
cls = ALL_NODE_CLASS_MAPPINGS['UltralyticsDetectorProvider']
|
||||
bbox_detector, segm_detector = cls().doit(model_name)
|
||||
pipe = (bbox_detector, bbox_threshold, bbox_dilation, bbox_crop_factor, segm_detector)
|
||||
return (pipe,)
|
||||
|
||||
class samLoaderForDetailerFix:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"model_name": (folder_paths.get_filename_list("sams"),),
|
||||
"device_mode": (["AUTO", "Prefer GPU", "CPU"],{"default": "AUTO"}),
|
||||
"sam_detection_hint": (
|
||||
["center-1", "horizontal-2", "vertical-2", "rect-4", "diamond-4", "mask-area", "mask-points",
|
||||
"mask-point-bbox", "none"],),
|
||||
"sam_dilation": ("INT", {"default": 0, "min": -512, "max": 512, "step": 1}),
|
||||
"sam_threshold": ("FLOAT", {"default": 0.93, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"sam_bbox_expansion": ("INT", {"default": 0, "min": 0, "max": 1000, "step": 1}),
|
||||
"sam_mask_hint_threshold": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"sam_mask_hint_use_negative": (["False", "Small", "Outter"],),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("sam_pipe",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "EasyUse/Fix"
|
||||
|
||||
def doit(self, model_name, device_mode, sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold, sam_mask_hint_use_negative):
|
||||
if 'SAMLoader' not in ALL_NODE_CLASS_MAPPINGS:
|
||||
raise Exception(f"[ERROR] To use SAMLoader, you need to install 'Impact Pack'")
|
||||
cls = ALL_NODE_CLASS_MAPPINGS['SAMLoader']
|
||||
(sam_model,) = cls().load_model(model_name, device_mode)
|
||||
pipe = (sam_model, sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold, sam_mask_hint_use_negative)
|
||||
return (pipe,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"easy hiresFix": hiresFix,
|
||||
"easy preDetailerFix": preDetailerFix,
|
||||
"easy preMaskDetailerFix": preMaskDetailerFix,
|
||||
"easy ultralyticsDetectorPipe": ultralyticsDetectorForDetailerFix,
|
||||
"easy samLoaderPipe": samLoaderForDetailerFix,
|
||||
"easy detailerFix": detailerFix
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy hiresFix": "HiresFix",
|
||||
"easy preDetailerFix": "PreDetailerFix",
|
||||
"easy preMaskDetailerFix": "preMaskDetailerFix",
|
||||
"easy ultralyticsDetectorPipe": "UltralyticsDetector (Pipe)",
|
||||
"easy samLoaderPipe": "SAMLoader (Pipe)",
|
||||
"easy detailerFix": "DetailerFix",
|
||||
}
|
||||
@@ -15,13 +15,13 @@ from PIL.PngImagePlugin import PngInfo
|
||||
import torch.nn.functional as F
|
||||
from torchvision.transforms import Resize, CenterCrop, GaussianBlur
|
||||
from torchvision.transforms.functional import to_pil_image
|
||||
from .libs.log import log_node_info
|
||||
from .libs.utils import AlwaysEqualProxy, ByPassTypeTuple
|
||||
from .libs.cache import cache, update_cache, remove_cache
|
||||
from .libs.image import pil2tensor, tensor2pil, ResizeMode, get_new_bounds, RGB2RGBA, image2mask, empty_image
|
||||
from .libs.colorfix import adain_color_fix, wavelet_color_fix
|
||||
from .libs.chooser import ChooserMessage, ChooserCancelled
|
||||
from .config import REMBG_DIR, REMBG_MODELS, HUMANPARSING_MODELS, MEDIAPIPE_MODELS, MEDIAPIPE_DIR
|
||||
from ..libs.log import log_node_info
|
||||
from ..libs.utils import AlwaysEqualProxy, ByPassTypeTuple
|
||||
from ..libs.cache import cache, update_cache, remove_cache
|
||||
from ..libs.image import pil2tensor, tensor2pil, ResizeMode, get_new_bounds, RGB2RGBA, image2mask, empty_image
|
||||
from ..libs.colorfix import adain_color_fix, wavelet_color_fix
|
||||
from ..libs.chooser import ChooserMessage, ChooserCancelled
|
||||
from ..config import REMBG_DIR, REMBG_MODELS, HUMANPARSING_MODELS, MEDIAPIPE_MODELS, MEDIAPIPE_DIR
|
||||
|
||||
any_type = AlwaysEqualProxy("*")
|
||||
# 图像数量
|
||||
@@ -819,8 +819,8 @@ class imageConcat:
|
||||
return (row,)
|
||||
|
||||
# 图片背景移除
|
||||
from .briaai.rembg import BriaRMBG, preprocess_image, postprocess_image
|
||||
from .libs.utils import get_local_filepath, easySave, install_package
|
||||
from ..modules.briaai.rembg import BriaRMBG, preprocess_image, postprocess_image
|
||||
from ..libs.utils import get_local_filepath, easySave, install_package
|
||||
class imageRemBg:
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
@@ -1197,7 +1197,7 @@ class imageDetailTransfer:
|
||||
"result": (new_image,)}
|
||||
|
||||
# 图像反推
|
||||
from .libs.image import ci
|
||||
from ..libs.image import ci
|
||||
class imageInterrogator:
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
@@ -1348,7 +1348,7 @@ class humanSegmentation:
|
||||
if method in cache:
|
||||
_, parsing = cache[method][1]
|
||||
else:
|
||||
from .human_parsing.run_parsing import HumanParsing
|
||||
from ..modules.human_parsing import HumanParsing
|
||||
onnx_path = os.path.join(folder_paths.models_dir, 'onnx')
|
||||
model_path = get_local_filepath(HUMANPARSING_MODELS['parsing_lip']['model_url'], onnx_path)
|
||||
parsing = HumanParsing(model_path=model_path)
|
||||
@@ -1370,7 +1370,7 @@ class humanSegmentation:
|
||||
if method in cache:
|
||||
_, parsing = cache[method][1]
|
||||
else:
|
||||
from .human_parsing.run_parsing import HumanParts
|
||||
from ..modules.human_parsing.run_parsing import HumanParts
|
||||
onnx_path = os.path.join(folder_paths.models_dir, 'onnx')
|
||||
human_parts_path = os.path.join(onnx_path, 'human-parts')
|
||||
model_path = get_local_filepath(HUMANPARSING_MODELS['human-parts']['model_url'], human_parts_path)
|
||||
@@ -0,0 +1,353 @@
|
||||
import re
|
||||
import torch
|
||||
import comfy
|
||||
from comfy_extras.nodes_mask import GrowMask
|
||||
from nodes import VAEEncodeForInpaint, NODE_CLASS_MAPPINGS as ALL_NODE_CLASS_MAPPINGS
|
||||
from ..libs.utils import get_local_filepath
|
||||
from ..libs.log import log_node_info
|
||||
from ..libs import cache as backend_cache
|
||||
from ..config import *
|
||||
|
||||
# FooocusInpaint
|
||||
class applyFooocusInpaint:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"model": ("MODEL",),
|
||||
"latent": ("LATENT",),
|
||||
"head": (list(FOOOCUS_INPAINT_HEAD.keys()),),
|
||||
"patch": (list(FOOOCUS_INPAINT_PATCH.keys()),),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MODEL",)
|
||||
RETURN_NAMES = ("model",)
|
||||
CATEGORY = "EasyUse/Inpaint"
|
||||
FUNCTION = "apply"
|
||||
|
||||
def apply(self, model, latent, head, patch):
|
||||
from ..modules.fooocus import InpaintHead, InpaintWorker
|
||||
head_file = get_local_filepath(FOOOCUS_INPAINT_HEAD[head]["model_url"], INPAINT_DIR)
|
||||
inpaint_head_model = InpaintHead()
|
||||
sd = torch.load(head_file, map_location='cpu')
|
||||
inpaint_head_model.load_state_dict(sd)
|
||||
|
||||
patch_file = get_local_filepath(FOOOCUS_INPAINT_PATCH[patch]["model_url"], INPAINT_DIR)
|
||||
inpaint_lora = comfy.utils.load_torch_file(patch_file, safe_load=True)
|
||||
|
||||
patch = (inpaint_head_model, inpaint_lora)
|
||||
worker = InpaintWorker(node_name="easy kSamplerInpainting")
|
||||
cloned = model.clone()
|
||||
|
||||
m, = worker.patch(cloned, latent, patch)
|
||||
return (m,)
|
||||
|
||||
# brushnet
|
||||
from ..modules.brushnet import BrushNet
|
||||
class applyBrushNet:
|
||||
|
||||
def get_files_with_extension(folder='inpaint', extensions='.safetensors'):
|
||||
return [file for file in folder_paths.get_filename_list(folder) if file.endswith(extensions)]
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"image": ("IMAGE",),
|
||||
"mask": ("MASK",),
|
||||
"brushnet": (s.get_files_with_extension(),),
|
||||
"dtype": (['float16', 'bfloat16', 'float32', 'float64'], ),
|
||||
"scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0}),
|
||||
"start_at": ("INT", {"default": 0, "min": 0, "max": 10000}),
|
||||
"end_at": ("INT", {"default": 10000, "min": 0, "max": 10000}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("pipe",)
|
||||
CATEGORY = "EasyUse/Inpaint"
|
||||
FUNCTION = "apply"
|
||||
|
||||
def apply(self, pipe, image, mask, brushnet, dtype, scale, start_at, end_at):
|
||||
|
||||
model = pipe['model']
|
||||
vae = pipe['vae']
|
||||
positive = pipe['positive']
|
||||
negative = pipe['negative']
|
||||
cls = BrushNet()
|
||||
if brushnet in backend_cache.cache:
|
||||
log_node_info("easy brushnetApply", f"Using {brushnet} Cached")
|
||||
_, brushnet_model = backend_cache.cache[brushnet][1]
|
||||
else:
|
||||
brushnet_file = os.path.join(folder_paths.get_full_path("inpaint", brushnet))
|
||||
brushnet_model, = cls.load_brushnet_model(brushnet_file, dtype)
|
||||
backend_cache.update_cache(brushnet, 'brushnet', (False, brushnet_model))
|
||||
m, positive, negative, latent = cls.brushnet_model_update(model=model, vae=vae, image=image, mask=mask,
|
||||
brushnet=brushnet_model, positive=positive,
|
||||
negative=negative, scale=scale, start_at=start_at,
|
||||
end_at=end_at)
|
||||
new_pipe = {
|
||||
**pipe,
|
||||
"model": m,
|
||||
"positive": positive,
|
||||
"negative": negative,
|
||||
"samples": latent,
|
||||
}
|
||||
del pipe
|
||||
return (new_pipe,)
|
||||
|
||||
# #powerpaint
|
||||
class applyPowerPaint:
|
||||
def get_files_with_extension(folder='inpaint', extensions='.safetensors'):
|
||||
return [file for file in folder_paths.get_filename_list(folder) if file.endswith(extensions)]
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"image": ("IMAGE",),
|
||||
"mask": ("MASK",),
|
||||
"powerpaint_model": (s.get_files_with_extension(),),
|
||||
"powerpaint_clip": (s.get_files_with_extension(extensions='.bin'),),
|
||||
"dtype": (['float16', 'bfloat16', 'float32', 'float64'],),
|
||||
"fitting": ("FLOAT", {"default": 1.0, "min": 0.3, "max": 1.0}),
|
||||
"function": (['text guided', 'shape guided', 'object removal', 'context aware', 'image outpainting'],),
|
||||
"scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0}),
|
||||
"start_at": ("INT", {"default": 0, "min": 0, "max": 10000}),
|
||||
"end_at": ("INT", {"default": 10000, "min": 0, "max": 10000}),
|
||||
"save_memory": (['none', 'auto', 'max'],),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("pipe",)
|
||||
CATEGORY = "EasyUse/Inpaint"
|
||||
FUNCTION = "apply"
|
||||
|
||||
def apply(self, pipe, image, mask, powerpaint_model, powerpaint_clip, dtype, fitting, function, scale, start_at, end_at, save_memory='none'):
|
||||
model = pipe['model']
|
||||
vae = pipe['vae']
|
||||
positive = pipe['positive']
|
||||
negative = pipe['negative']
|
||||
|
||||
cls = BrushNet()
|
||||
# load powerpaint clip
|
||||
if powerpaint_clip in backend_cache.cache:
|
||||
log_node_info("easy powerpaintApply", f"Using {powerpaint_clip} Cached")
|
||||
_, ppclip = backend_cache.cache[powerpaint_clip][1]
|
||||
else:
|
||||
model_url = POWERPAINT_MODELS['base_fp16']['model_url']
|
||||
base_clip = get_local_filepath(model_url, os.path.join(folder_paths.models_dir, 'clip'))
|
||||
ppclip, = cls.load_powerpaint_clip(base_clip, os.path.join(folder_paths.get_full_path("inpaint", powerpaint_clip)))
|
||||
backend_cache.update_cache(powerpaint_clip, 'ppclip', (False, ppclip))
|
||||
# load powerpaint model
|
||||
if powerpaint_model in backend_cache.cache:
|
||||
log_node_info("easy powerpaintApply", f"Using {powerpaint_model} Cached")
|
||||
_, powerpaint = backend_cache.cache[powerpaint_model][1]
|
||||
else:
|
||||
powerpaint_file = os.path.join(folder_paths.get_full_path("inpaint", powerpaint_model))
|
||||
powerpaint, = cls.load_brushnet_model(powerpaint_file, dtype)
|
||||
backend_cache.update_cache(powerpaint_model, 'powerpaint', (False, powerpaint))
|
||||
m, positive, negative, latent = cls.powerpaint_model_update(model=model, vae=vae, image=image, mask=mask, powerpaint=powerpaint,
|
||||
clip=ppclip, positive=positive,
|
||||
negative=negative, fitting=fitting, function=function,
|
||||
scale=scale, start_at=start_at, end_at=end_at, save_memory=save_memory)
|
||||
new_pipe = {
|
||||
**pipe,
|
||||
"model": m,
|
||||
"positive": positive,
|
||||
"negative": negative,
|
||||
"samples": latent,
|
||||
}
|
||||
del pipe
|
||||
return (new_pipe,)
|
||||
|
||||
from node_helpers import conditioning_set_values
|
||||
class applyInpaint:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"image": ("IMAGE",),
|
||||
"mask": ("MASK",),
|
||||
"inpaint_mode": (('normal', 'fooocus_inpaint', 'brushnet_random', 'brushnet_segmentation', 'powerpaint'),),
|
||||
"encode": (('none', 'vae_encode_inpaint', 'inpaint_model_conditioning', 'different_diffusion'), {"default": "none"}),
|
||||
"grow_mask_by": ("INT", {"default": 6, "min": 0, "max": 64, "step": 1}),
|
||||
"dtype": (['float16', 'bfloat16', 'float32', 'float64'],),
|
||||
"fitting": ("FLOAT", {"default": 1.0, "min": 0.3, "max": 1.0}),
|
||||
"function": (['text guided', 'shape guided', 'object removal', 'context aware', 'image outpainting'],),
|
||||
"scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0}),
|
||||
"start_at": ("INT", {"default": 0, "min": 0, "max": 10000}),
|
||||
"end_at": ("INT", {"default": 10000, "min": 0, "max": 10000}),
|
||||
},
|
||||
"optional":{
|
||||
"noise_mask": ("BOOLEAN", {"default": True})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("pipe",)
|
||||
CATEGORY = "EasyUse/Inpaint"
|
||||
FUNCTION = "apply"
|
||||
|
||||
def inpaint_model_conditioning(self, pipe, image, vae, mask, grow_mask_by, noise_mask=True):
|
||||
if grow_mask_by >0:
|
||||
mask, = GrowMask().expand_mask(mask, grow_mask_by, False)
|
||||
positive, negative, = pipe['positive'], pipe['negative']
|
||||
|
||||
pixels = image
|
||||
x = (pixels.shape[1] // 8) * 8
|
||||
y = (pixels.shape[2] // 8) * 8
|
||||
mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])),
|
||||
size=(pixels.shape[1], pixels.shape[2]), mode="bilinear")
|
||||
|
||||
orig_pixels = pixels
|
||||
pixels = orig_pixels.clone()
|
||||
if pixels.shape[1] != x or pixels.shape[2] != y:
|
||||
x_offset = (pixels.shape[1] % 8) // 2
|
||||
y_offset = (pixels.shape[2] % 8) // 2
|
||||
pixels = pixels[:, x_offset:x + x_offset, y_offset:y + y_offset, :]
|
||||
mask = mask[:, :, x_offset:x + x_offset, y_offset:y + y_offset]
|
||||
|
||||
m = (1.0 - mask.round()).squeeze(1)
|
||||
for i in range(3):
|
||||
pixels[:, :, :, i] -= 0.5
|
||||
pixels[:, :, :, i] *= m
|
||||
pixels[:, :, :, i] += 0.5
|
||||
concat_latent = vae.encode(pixels)
|
||||
orig_latent = vae.encode(orig_pixels)
|
||||
|
||||
out_latent = {}
|
||||
|
||||
out_latent["samples"] = orig_latent
|
||||
if noise_mask:
|
||||
out_latent["noise_mask"] = mask
|
||||
|
||||
out = []
|
||||
for conditioning in [positive, negative]:
|
||||
c = conditioning_set_values(conditioning, {"concat_latent_image": concat_latent,
|
||||
"concat_mask": mask})
|
||||
out.append(c)
|
||||
|
||||
pipe['positive'] = out[0]
|
||||
pipe['negative'] = out[1]
|
||||
pipe['samples'] = out_latent
|
||||
|
||||
return pipe
|
||||
|
||||
def get_brushnet_model(self, type, model):
|
||||
model_type = 'sdxl' if isinstance(model.model.model_config, comfy.supported_models.SDXL) else 'sd1'
|
||||
if type == 'brushnet_random':
|
||||
brush_model = BRUSHNET_MODELS['random_mask'][model_type]['model_url']
|
||||
if model_type == 'sdxl':
|
||||
pattern = 'brushnet.random.mask.sdxl.*.(safetensors|bin)$'
|
||||
else:
|
||||
pattern = 'brushnet.random.mask.*.(safetensors|bin)$'
|
||||
elif type == 'brushnet_segmentation':
|
||||
brush_model = BRUSHNET_MODELS['segmentation_mask'][model_type]['model_url']
|
||||
if model_type == 'sdxl':
|
||||
pattern = 'brushnet.segmentation.mask.sdxl.*.(safetensors|bin)$'
|
||||
else:
|
||||
pattern = 'brushnet.segmentation.mask.*.(safetensors|bin)$'
|
||||
|
||||
|
||||
brushfile = [e for e in folder_paths.get_filename_list('inpaint') if re.search(pattern, e, re.IGNORECASE)]
|
||||
brushname = brushfile[0] if brushfile else None
|
||||
if not brushname:
|
||||
from urllib.parse import urlparse
|
||||
get_local_filepath(brush_model, INPAINT_DIR)
|
||||
parsed_url = urlparse(brush_model)
|
||||
brushname = os.path.basename(parsed_url.path)
|
||||
return brushname
|
||||
|
||||
def get_powerpaint_model(self, model):
|
||||
model_type = 'sdxl' if isinstance(model.model.model_config, comfy.supported_models.SDXL) else 'sd1'
|
||||
if model_type == 'sdxl':
|
||||
raise Exception("Powerpaint not supported for SDXL models")
|
||||
|
||||
powerpaint_model = POWERPAINT_MODELS['v2.1']['model_url']
|
||||
powerpaint_clip = POWERPAINT_MODELS['v2.1']['clip_url']
|
||||
|
||||
from urllib.parse import urlparse
|
||||
get_local_filepath(powerpaint_model, os.path.join(INPAINT_DIR, 'powerpaint'))
|
||||
model_parsed_url = urlparse(powerpaint_model)
|
||||
clip_parsed_url = urlparse(powerpaint_clip)
|
||||
model_name = os.path.join("powerpaint",os.path.basename(model_parsed_url.path))
|
||||
clip_name = os.path.join("powerpaint",os.path.basename(clip_parsed_url.path))
|
||||
return model_name, clip_name
|
||||
|
||||
def apply(self, pipe, image, mask, inpaint_mode, encode, grow_mask_by, dtype, fitting, function, scale, start_at, end_at, noise_mask=True):
|
||||
new_pipe = {
|
||||
**pipe,
|
||||
}
|
||||
del pipe
|
||||
if inpaint_mode in ['brushnet_random', 'brushnet_segmentation']:
|
||||
brushnet = self.get_brushnet_model(inpaint_mode, new_pipe['model'])
|
||||
new_pipe, = applyBrushNet().apply(new_pipe, image, mask, brushnet, dtype, scale, start_at, end_at)
|
||||
elif inpaint_mode == 'powerpaint':
|
||||
powerpaint_model, powerpaint_clip = self.get_powerpaint_model(new_pipe['model'])
|
||||
new_pipe, = applyPowerPaint().apply(new_pipe, image, mask, powerpaint_model, powerpaint_clip, dtype, fitting, function, scale, start_at, end_at)
|
||||
|
||||
vae = new_pipe['vae']
|
||||
if encode == 'none':
|
||||
if inpaint_mode == 'fooocus_inpaint':
|
||||
model, = applyFooocusInpaint().apply(new_pipe['model'], new_pipe['samples'],
|
||||
list(FOOOCUS_INPAINT_HEAD.keys())[0],
|
||||
list(FOOOCUS_INPAINT_PATCH.keys())[0])
|
||||
new_pipe['model'] = model
|
||||
elif encode == 'vae_encode_inpaint':
|
||||
latent, = VAEEncodeForInpaint().encode(vae, image, mask, grow_mask_by)
|
||||
new_pipe['samples'] = latent
|
||||
if inpaint_mode == 'fooocus_inpaint':
|
||||
model, = applyFooocusInpaint().apply(new_pipe['model'], new_pipe['samples'],
|
||||
list(FOOOCUS_INPAINT_HEAD.keys())[0],
|
||||
list(FOOOCUS_INPAINT_PATCH.keys())[0])
|
||||
new_pipe['model'] = model
|
||||
elif encode == 'inpaint_model_conditioning':
|
||||
if inpaint_mode == 'fooocus_inpaint':
|
||||
latent, = VAEEncodeForInpaint().encode(vae, image, mask, grow_mask_by)
|
||||
new_pipe['samples'] = latent
|
||||
model, = applyFooocusInpaint().apply(new_pipe['model'], new_pipe['samples'],
|
||||
list(FOOOCUS_INPAINT_HEAD.keys())[0],
|
||||
list(FOOOCUS_INPAINT_PATCH.keys())[0])
|
||||
new_pipe['model'] = model
|
||||
new_pipe = self.inpaint_model_conditioning(new_pipe, image, vae, mask, 0, noise_mask=noise_mask)
|
||||
else:
|
||||
new_pipe = self.inpaint_model_conditioning(new_pipe, image, vae, mask, grow_mask_by, noise_mask=noise_mask)
|
||||
elif encode == 'different_diffusion':
|
||||
if inpaint_mode == 'fooocus_inpaint':
|
||||
latent, = VAEEncodeForInpaint().encode(vae, image, mask, grow_mask_by)
|
||||
new_pipe['samples'] = latent
|
||||
model, = applyFooocusInpaint().apply(new_pipe['model'], new_pipe['samples'],
|
||||
list(FOOOCUS_INPAINT_HEAD.keys())[0],
|
||||
list(FOOOCUS_INPAINT_PATCH.keys())[0])
|
||||
new_pipe['model'] = model
|
||||
new_pipe = self.inpaint_model_conditioning(new_pipe, image, vae, mask, 0, noise_mask=noise_mask)
|
||||
else:
|
||||
new_pipe = self.inpaint_model_conditioning(new_pipe, image, vae, mask, grow_mask_by, noise_mask=noise_mask)
|
||||
cls = ALL_NODE_CLASS_MAPPINGS['DifferentialDiffusion']
|
||||
if cls is not None:
|
||||
model, = cls().apply(new_pipe['model'])
|
||||
new_pipe['model'] = model
|
||||
else:
|
||||
raise Exception("Differential Diffusion not found,please update comfyui")
|
||||
|
||||
return (new_pipe,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"easy applyFooocusInpaint": applyFooocusInpaint,
|
||||
"easy applyBrushNet": applyBrushNet,
|
||||
"easy applyPowerPaint": applyPowerPaint,
|
||||
"easy applyInpaint": applyInpaint
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy applyFooocusInpaint": "Easy Apply Fooocus Inpaint",
|
||||
"easy applyBrushNet": "Easy Apply BrushNet",
|
||||
"easy applyPowerPaint": "Easy Apply PowerPaint",
|
||||
"easy applyInpaint": "Easy Apply Inpaint"
|
||||
}
|
||||
+1509
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
from typing import Iterator, List, Tuple, Dict, Any, Union, Optional
|
||||
from _decimal import Context, getcontext
|
||||
from decimal import Decimal
|
||||
from .libs.utils import AlwaysEqualProxy, ByPassTypeTuple, cleanGPUUsedForce, compare_revision
|
||||
from .libs.cache import cache, update_cache, remove_cache
|
||||
from .libs.log import log_node_info, log_node_warn
|
||||
from nodes import PreviewImage, SaveImage, NODE_CLASS_MAPPINGS as ALL_NODE_CLASS_MAPPINGS
|
||||
from PIL import Image, ImageDraw, ImageFilter, ImageOps
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
from ..libs.utils import AlwaysEqualProxy, ByPassTypeTuple, cleanGPUUsedForce, compare_revision
|
||||
from ..libs.cache import cache, update_cache, remove_cache
|
||||
from ..libs.log import log_node_info, log_node_warn
|
||||
import numpy as np
|
||||
import time
|
||||
import os
|
||||
@@ -23,7 +23,6 @@ lazy_options = {"lazy": True} if compare_revision(2543) else {}
|
||||
|
||||
any_type = AlwaysEqualProxy("*")
|
||||
|
||||
|
||||
def validate_list_args(args: Dict[str, List[Any]]) -> Tuple[bool, Optional[str], Optional[str]]:
|
||||
"""
|
||||
Checks that if there are multiple arguments, they are all the same length or 1
|
||||
@@ -1088,7 +1087,7 @@ class isFileExist:
|
||||
|
||||
|
||||
from nodes import MAX_RESOLUTION
|
||||
from .config import BASE_RESOLUTIONS
|
||||
from ..config import BASE_RESOLUTIONS
|
||||
|
||||
|
||||
class pixels:
|
||||
@@ -0,0 +1,778 @@
|
||||
import os
|
||||
import folder_paths
|
||||
import comfy.samplers, comfy.supported_models
|
||||
|
||||
from nodes import LatentFromBatch, RepeatLatentBatch
|
||||
from ..config import MAX_SEED_NUM
|
||||
|
||||
from ..libs.log import log_node_warn
|
||||
from ..libs.utils import get_sd_version
|
||||
from ..libs.conditioning import prompt_to_cond, set_cond
|
||||
|
||||
from .. import easyCache
|
||||
|
||||
# 节点束输入
|
||||
class pipeIn:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"model": ("MODEL",),
|
||||
"pos": ("CONDITIONING",),
|
||||
"neg": ("CONDITIONING",),
|
||||
"latent": ("LATENT",),
|
||||
"vae": ("VAE",),
|
||||
"clip": ("CLIP",),
|
||||
"image": ("IMAGE",),
|
||||
"xyPlot": ("XYPLOT",),
|
||||
},
|
||||
"hidden": {"my_unique_id": "UNIQUE_ID"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("pipe",)
|
||||
FUNCTION = "flush"
|
||||
|
||||
CATEGORY = "EasyUse/Pipe"
|
||||
|
||||
def flush(self, pipe=None, model=None, pos=None, neg=None, latent=None, vae=None, clip=None, image=None, xyplot=None, my_unique_id=None):
|
||||
|
||||
model = model if model is not None else pipe.get("model")
|
||||
if model is None:
|
||||
log_node_warn(f'pipeIn[{my_unique_id}]', "Model missing from pipeLine")
|
||||
pos = pos if pos is not None else pipe.get("positive")
|
||||
if pos is None:
|
||||
log_node_warn(f'pipeIn[{my_unique_id}]', "Pos Conditioning missing from pipeLine")
|
||||
neg = neg if neg is not None else pipe.get("negative")
|
||||
if neg is None:
|
||||
log_node_warn(f'pipeIn[{my_unique_id}]', "Neg Conditioning missing from pipeLine")
|
||||
vae = vae if vae is not None else pipe.get("vae")
|
||||
if vae is None:
|
||||
log_node_warn(f'pipeIn[{my_unique_id}]', "VAE missing from pipeLine")
|
||||
clip = clip if clip is not None else pipe.get("clip") if pipe is not None and "clip" in pipe else None
|
||||
# if clip is None:
|
||||
# log_node_warn(f'pipeIn[{my_unique_id}]', "Clip missing from pipeLine")
|
||||
if latent is not None:
|
||||
samples = latent
|
||||
elif image is None:
|
||||
samples = pipe.get("samples") if pipe is not None else None
|
||||
image = pipe.get("images") if pipe is not None else None
|
||||
elif image is not None:
|
||||
if pipe is None:
|
||||
batch_size = 1
|
||||
else:
|
||||
batch_size = pipe["loader_settings"]["batch_size"] if "batch_size" in pipe["loader_settings"] else 1
|
||||
samples = {"samples": vae.encode(image[:, :, :, :3])}
|
||||
samples = RepeatLatentBatch().repeat(samples, batch_size)[0]
|
||||
|
||||
if pipe is None:
|
||||
pipe = {"loader_settings": {"positive": "", "negative": "", "xyplot": None}}
|
||||
|
||||
xyplot = xyplot if xyplot is not None else pipe['loader_settings']['xyplot'] if xyplot in pipe['loader_settings'] else None
|
||||
|
||||
new_pipe = {
|
||||
**pipe,
|
||||
"model": model,
|
||||
"positive": pos,
|
||||
"negative": neg,
|
||||
"vae": vae,
|
||||
"clip": clip,
|
||||
|
||||
"samples": samples,
|
||||
"images": image,
|
||||
"seed": pipe.get('seed') if pipe is not None and "seed" in pipe else None,
|
||||
|
||||
"loader_settings": {
|
||||
**pipe["loader_settings"],
|
||||
"xyplot": xyplot
|
||||
}
|
||||
}
|
||||
del pipe
|
||||
|
||||
return (new_pipe,)
|
||||
|
||||
# 节点束输出
|
||||
class pipeOut:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
},
|
||||
"hidden": {"my_unique_id": "UNIQUE_ID"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE", "MODEL", "CONDITIONING", "CONDITIONING", "LATENT", "VAE", "CLIP", "IMAGE", "INT",)
|
||||
RETURN_NAMES = ("pipe", "model", "pos", "neg", "latent", "vae", "clip", "image", "seed",)
|
||||
FUNCTION = "flush"
|
||||
|
||||
CATEGORY = "EasyUse/Pipe"
|
||||
|
||||
def flush(self, pipe, my_unique_id=None):
|
||||
model = pipe.get("model")
|
||||
pos = pipe.get("positive")
|
||||
neg = pipe.get("negative")
|
||||
latent = pipe.get("samples")
|
||||
vae = pipe.get("vae")
|
||||
clip = pipe.get("clip")
|
||||
image = pipe.get("images")
|
||||
seed = pipe.get("seed")
|
||||
|
||||
return pipe, model, pos, neg, latent, vae, clip, image, seed
|
||||
|
||||
# 编辑节点束
|
||||
class pipeEdit:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"clip_skip": ("INT", {"default": -1, "min": -24, "max": 0, "step": 1}),
|
||||
|
||||
"optional_positive": ("STRING", {"default": "", "multiline": True}),
|
||||
"positive_token_normalization": (["none", "mean", "length", "length+mean"],),
|
||||
"positive_weight_interpretation": (["comfy", "A1111", "comfy++", "compel", "fixed attention"],),
|
||||
|
||||
"optional_negative": ("STRING", {"default": "", "multiline": True}),
|
||||
"negative_token_normalization": (["none", "mean", "length", "length+mean"],),
|
||||
"negative_weight_interpretation": (["comfy", "A1111", "comfy++", "compel", "fixed attention"],),
|
||||
|
||||
"a1111_prompt_style": ("BOOLEAN", {"default": False}),
|
||||
"conditioning_mode": (['replace', 'concat', 'combine', 'average', 'timestep'], {"default": "replace"}),
|
||||
"average_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"old_cond_start": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
||||
"old_cond_end": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
||||
"new_cond_start": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
||||
"new_cond_end": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
||||
},
|
||||
"optional": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"model": ("MODEL",),
|
||||
"pos": ("CONDITIONING",),
|
||||
"neg": ("CONDITIONING",),
|
||||
"latent": ("LATENT",),
|
||||
"vae": ("VAE",),
|
||||
"clip": ("CLIP",),
|
||||
"image": ("IMAGE",),
|
||||
},
|
||||
"hidden": {"my_unique_id": "UNIQUE_ID", "prompt":"PROMPT"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE", "MODEL", "CONDITIONING", "CONDITIONING", "LATENT", "VAE", "CLIP", "IMAGE")
|
||||
RETURN_NAMES = ("pipe", "model", "pos", "neg", "latent", "vae", "clip", "image")
|
||||
FUNCTION = "edit"
|
||||
|
||||
CATEGORY = "EasyUse/Pipe"
|
||||
|
||||
def edit(self, clip_skip, optional_positive, positive_token_normalization, positive_weight_interpretation, optional_negative, negative_token_normalization, negative_weight_interpretation, a1111_prompt_style, conditioning_mode, average_strength, old_cond_start, old_cond_end, new_cond_start, new_cond_end, pipe=None, model=None, pos=None, neg=None, latent=None, vae=None, clip=None, image=None, my_unique_id=None, prompt=None):
|
||||
|
||||
model = model if model is not None else pipe.get("model")
|
||||
if model is None:
|
||||
log_node_warn(f'pipeIn[{my_unique_id}]', "Model missing from pipeLine")
|
||||
vae = vae if vae is not None else pipe.get("vae")
|
||||
if vae is None:
|
||||
log_node_warn(f'pipeIn[{my_unique_id}]', "VAE missing from pipeLine")
|
||||
clip = clip if clip is not None else pipe.get("clip")
|
||||
if clip is None:
|
||||
log_node_warn(f'pipeIn[{my_unique_id}]', "Clip missing from pipeLine")
|
||||
if image is None:
|
||||
image = pipe.get("images") if pipe is not None else None
|
||||
samples = latent if latent is not None else pipe.get("samples")
|
||||
if samples is None:
|
||||
log_node_warn(f'pipeIn[{my_unique_id}]', "Latent missing from pipeLine")
|
||||
else:
|
||||
batch_size = pipe["loader_settings"]["batch_size"] if "batch_size" in pipe["loader_settings"] else 1
|
||||
samples = {"samples": vae.encode(image[:, :, :, :3])}
|
||||
samples = RepeatLatentBatch().repeat(samples, batch_size)[0]
|
||||
|
||||
pipe_lora_stack = pipe.get("lora_stack") if pipe is not None and "lora_stack" in pipe else []
|
||||
|
||||
steps = pipe["loader_settings"]["steps"] if "steps" in pipe["loader_settings"] else 1
|
||||
if pos is None and optional_positive != '':
|
||||
pos, positive_wildcard_prompt, model, clip = prompt_to_cond('positive', model, clip, clip_skip,
|
||||
pipe_lora_stack, optional_positive, positive_token_normalization,positive_weight_interpretation,
|
||||
a1111_prompt_style, my_unique_id, prompt, easyCache, True, steps)
|
||||
pos = set_cond(pipe['positive'], pos, conditioning_mode, average_strength, old_cond_start, old_cond_end, new_cond_start, new_cond_end)
|
||||
pipe['loader_settings']['positive'] = positive_wildcard_prompt
|
||||
pipe['loader_settings']['positive_token_normalization'] = positive_token_normalization
|
||||
pipe['loader_settings']['positive_weight_interpretation'] = positive_weight_interpretation
|
||||
if a1111_prompt_style:
|
||||
pipe['loader_settings']['a1111_prompt_style'] = True
|
||||
else:
|
||||
pos = pipe.get("positive")
|
||||
if pos is None:
|
||||
log_node_warn(f'pipeIn[{my_unique_id}]', "Pos Conditioning missing from pipeLine")
|
||||
|
||||
if neg is None and optional_negative != '':
|
||||
neg, negative_wildcard_prompt, model, clip = prompt_to_cond("negative", model, clip, clip_skip, pipe_lora_stack, optional_negative,
|
||||
negative_token_normalization, negative_weight_interpretation,
|
||||
a1111_prompt_style, my_unique_id, prompt, easyCache, True, steps)
|
||||
neg = set_cond(pipe['negative'], neg, conditioning_mode, average_strength, old_cond_start, old_cond_end, new_cond_start, new_cond_end)
|
||||
pipe['loader_settings']['negative'] = negative_wildcard_prompt
|
||||
pipe['loader_settings']['negative_token_normalization'] = negative_token_normalization
|
||||
pipe['loader_settings']['negative_weight_interpretation'] = negative_weight_interpretation
|
||||
if a1111_prompt_style:
|
||||
pipe['loader_settings']['a1111_prompt_style'] = True
|
||||
else:
|
||||
neg = pipe.get("negative")
|
||||
if neg is None:
|
||||
log_node_warn(f'pipeIn[{my_unique_id}]', "Neg Conditioning missing from pipeLine")
|
||||
if pipe is None:
|
||||
pipe = {"loader_settings": {"positive": "", "negative": "", "xyplot": None}}
|
||||
|
||||
new_pipe = {
|
||||
**pipe,
|
||||
"model": model,
|
||||
"positive": pos,
|
||||
"negative": neg,
|
||||
"vae": vae,
|
||||
"clip": clip,
|
||||
|
||||
"samples": samples,
|
||||
"images": image,
|
||||
"seed": pipe.get('seed') if pipe is not None and "seed" in pipe else None,
|
||||
"loader_settings":{
|
||||
**pipe["loader_settings"]
|
||||
}
|
||||
}
|
||||
del pipe
|
||||
|
||||
return (new_pipe, model,pos, neg, latent, vae, clip, image)
|
||||
|
||||
# 编辑节点束提示词
|
||||
class pipeEditPrompt:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"positive": ("STRING", {"default": "", "multiline": True}),
|
||||
"negative": ("STRING", {"default": "", "multiline": True}),
|
||||
},
|
||||
"hidden": {"my_unique_id": "UNIQUE_ID", "prompt": "PROMPT"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("pipe",)
|
||||
FUNCTION = "edit"
|
||||
|
||||
CATEGORY = "EasyUse/Pipe"
|
||||
|
||||
def edit(self, pipe, positive, negative, my_unique_id=None, prompt=None):
|
||||
model = pipe.get("model")
|
||||
if model is None:
|
||||
log_node_warn(f'pipeEdit[{my_unique_id}]', "Model missing from pipeLine")
|
||||
|
||||
from ..modules.kolors.loader import is_kolors_model
|
||||
model_type = get_sd_version(model)
|
||||
if model_type == 'sdxl' and is_kolors_model(model):
|
||||
from ..modules.kolors.text_encode import chatglm3_adv_text_encode
|
||||
auto_clean_gpu = pipe["loader_settings"]["auto_clean_gpu"] if "auto_clean_gpu" in pipe["loader_settings"] else False
|
||||
chatglm3_model = pipe["chatglm3_model"] if "chatglm3_model" in pipe else None
|
||||
# text encode
|
||||
log_node_warn("Positive encoding...")
|
||||
positive_embeddings_final = chatglm3_adv_text_encode(chatglm3_model, positive, auto_clean_gpu)
|
||||
log_node_warn("Negative encoding...")
|
||||
negative_embeddings_final = chatglm3_adv_text_encode(chatglm3_model, negative, auto_clean_gpu)
|
||||
else:
|
||||
clip_skip = pipe["loader_settings"]["clip_skip"] if "clip_skip" in pipe["loader_settings"] else -1
|
||||
lora_stack = pipe.get("lora_stack") if pipe is not None and "lora_stack" in pipe else []
|
||||
clip = pipe.get("clip") if pipe is not None and "clip" in pipe else None
|
||||
positive_token_normalization = pipe["loader_settings"]["positive_token_normalization"] if "positive_token_normalization" in pipe["loader_settings"] else "none"
|
||||
positive_weight_interpretation = pipe["loader_settings"]["positive_weight_interpretation"] if "positive_weight_interpretation" in pipe["loader_settings"] else "comfy"
|
||||
negative_token_normalization = pipe["loader_settings"]["negative_token_normalization"] if "negative_token_normalization" in pipe["loader_settings"] else "none"
|
||||
negative_weight_interpretation = pipe["loader_settings"]["negative_weight_interpretation"] if "negative_weight_interpretation" in pipe["loader_settings"] else "comfy"
|
||||
a1111_prompt_style = pipe["loader_settings"]["a1111_prompt_style"] if "a1111_prompt_style" in pipe["loader_settings"] else False
|
||||
# Prompt to Conditioning
|
||||
positive_embeddings_final, positive_wildcard_prompt, model, clip = prompt_to_cond('positive', model, clip,
|
||||
clip_skip, lora_stack,
|
||||
positive,
|
||||
positive_token_normalization,
|
||||
positive_weight_interpretation,
|
||||
a1111_prompt_style,
|
||||
my_unique_id, prompt,
|
||||
easyCache,
|
||||
model_type=model_type)
|
||||
negative_embeddings_final, negative_wildcard_prompt, model, clip = prompt_to_cond('negative', model, clip,
|
||||
clip_skip, lora_stack,
|
||||
negative,
|
||||
negative_token_normalization,
|
||||
negative_weight_interpretation,
|
||||
a1111_prompt_style,
|
||||
my_unique_id, prompt,
|
||||
easyCache,
|
||||
model_type=model_type)
|
||||
new_pipe = {
|
||||
**pipe,
|
||||
"model": model,
|
||||
"positive": positive_embeddings_final,
|
||||
"negative": negative_embeddings_final,
|
||||
}
|
||||
del pipe
|
||||
|
||||
return (new_pipe,)
|
||||
|
||||
|
||||
# 节点束到基础节点束(pipe to ComfyUI-Impack-pack's basic_pipe)
|
||||
class pipeToBasicPipe:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
},
|
||||
"hidden": {"my_unique_id": "UNIQUE_ID"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BASIC_PIPE",)
|
||||
RETURN_NAMES = ("basic_pipe",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "EasyUse/Pipe"
|
||||
|
||||
def doit(self, pipe, my_unique_id=None):
|
||||
new_pipe = (pipe.get('model'), pipe.get('clip'), pipe.get('vae'), pipe.get('positive'), pipe.get('negative'))
|
||||
del pipe
|
||||
return (new_pipe,)
|
||||
|
||||
# 批次索引
|
||||
class pipeBatchIndex:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {"pipe": ("PIPE_LINE",),
|
||||
"batch_index": ("INT", {"default": 0, "min": 0, "max": 63}),
|
||||
"length": ("INT", {"default": 1, "min": 1, "max": 64}),
|
||||
},
|
||||
"hidden": {"my_unique_id": "UNIQUE_ID"},}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("pipe",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "EasyUse/Pipe"
|
||||
|
||||
def doit(self, pipe, batch_index, length, my_unique_id=None):
|
||||
samples = pipe["samples"]
|
||||
new_samples, = LatentFromBatch().frombatch(samples, batch_index, length)
|
||||
new_pipe = {
|
||||
**pipe,
|
||||
"samples": new_samples
|
||||
}
|
||||
del pipe
|
||||
return (new_pipe,)
|
||||
|
||||
# pipeXYPlot
|
||||
class pipeXYPlot:
|
||||
lora_list = ["None"] + folder_paths.get_filename_list("loras")
|
||||
lora_strengths = {"min": -4.0, "max": 4.0, "step": 0.01}
|
||||
token_normalization = ["none", "mean", "length", "length+mean"]
|
||||
weight_interpretation = ["comfy", "A1111", "compel", "comfy++"]
|
||||
|
||||
loader_dict = {
|
||||
"ckpt_name": folder_paths.get_filename_list("checkpoints"),
|
||||
"vae_name": ["Baked-VAE"] + folder_paths.get_filename_list("vae"),
|
||||
"clip_skip": {"min": -24, "max": -1, "step": 1},
|
||||
"lora_name": lora_list,
|
||||
"lora_model_strength": lora_strengths,
|
||||
"lora_clip_strength": lora_strengths,
|
||||
"positive": [],
|
||||
"negative": [],
|
||||
}
|
||||
|
||||
sampler_dict = {
|
||||
"steps": {"min": 1, "max": 100, "step": 1},
|
||||
"cfg": {"min": 0.0, "max": 100.0, "step": 1.0},
|
||||
"sampler_name": comfy.samplers.KSampler.SAMPLERS,
|
||||
"scheduler": comfy.samplers.KSampler.SCHEDULERS,
|
||||
"denoise": {"min": 0.0, "max": 1.0, "step": 0.01},
|
||||
"seed": {"min": 0, "max": MAX_SEED_NUM},
|
||||
}
|
||||
|
||||
plot_dict = {**sampler_dict, **loader_dict}
|
||||
|
||||
plot_values = ["None", ]
|
||||
plot_values.append("---------------------")
|
||||
for k in sampler_dict:
|
||||
plot_values.append(f'preSampling: {k}')
|
||||
plot_values.append("---------------------")
|
||||
for k in loader_dict:
|
||||
plot_values.append(f'loader: {k}')
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
rejected = ["None", "---------------------", "Nothing"]
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"grid_spacing": ("INT", {"min": 0, "max": 500, "step": 5, "default": 0, }),
|
||||
"output_individuals": (["False", "True"], {"default": "False"}),
|
||||
"flip_xy": (["False", "True"], {"default": "False"}),
|
||||
"x_axis": (pipeXYPlot.plot_values, {"default": 'None'}),
|
||||
"x_values": (
|
||||
"STRING", {"default": '', "multiline": True, "placeholder": 'insert values seperated by "; "'}),
|
||||
"y_axis": (pipeXYPlot.plot_values, {"default": 'None'}),
|
||||
"y_values": (
|
||||
"STRING", {"default": '', "multiline": True, "placeholder": 'insert values seperated by "; "'}),
|
||||
},
|
||||
"optional": {
|
||||
"pipe": ("PIPE_LINE",)
|
||||
},
|
||||
"hidden": {
|
||||
"plot_dict": (pipeXYPlot.plot_dict,),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("pipe",)
|
||||
FUNCTION = "plot"
|
||||
|
||||
CATEGORY = "EasyUse/Pipe"
|
||||
|
||||
def plot(self, grid_spacing, output_individuals, flip_xy, x_axis, x_values, y_axis, y_values, pipe=None, font_path=None):
|
||||
def clean_values(values):
|
||||
original_values = values.split("; ")
|
||||
cleaned_values = []
|
||||
|
||||
for value in original_values:
|
||||
# Strip the semi-colon
|
||||
cleaned_value = value.strip(';').strip()
|
||||
|
||||
if cleaned_value == "":
|
||||
continue
|
||||
|
||||
# Try to convert the cleaned_value back to int or float if possible
|
||||
try:
|
||||
cleaned_value = int(cleaned_value)
|
||||
except ValueError:
|
||||
try:
|
||||
cleaned_value = float(cleaned_value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Append the cleaned_value to the list
|
||||
cleaned_values.append(cleaned_value)
|
||||
|
||||
return cleaned_values
|
||||
|
||||
if x_axis in self.rejected:
|
||||
x_axis = "None"
|
||||
x_values = []
|
||||
else:
|
||||
x_values = clean_values(x_values)
|
||||
|
||||
if y_axis in self.rejected:
|
||||
y_axis = "None"
|
||||
y_values = []
|
||||
else:
|
||||
y_values = clean_values(y_values)
|
||||
|
||||
if flip_xy == "True":
|
||||
x_axis, y_axis = y_axis, x_axis
|
||||
x_values, y_values = y_values, x_values
|
||||
|
||||
|
||||
xy_plot = {"x_axis": x_axis,
|
||||
"x_vals": x_values,
|
||||
"y_axis": y_axis,
|
||||
"y_vals": y_values,
|
||||
"custom_font": font_path,
|
||||
"grid_spacing": grid_spacing,
|
||||
"output_individuals": output_individuals}
|
||||
|
||||
if pipe is not None:
|
||||
new_pipe = pipe
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"xyplot": xy_plot
|
||||
}
|
||||
del pipe
|
||||
return (new_pipe, xy_plot,)
|
||||
|
||||
# pipeXYPlotAdvanced
|
||||
import platform
|
||||
class pipeXYPlotAdvanced:
|
||||
if platform.system() == "Windows":
|
||||
system_root = os.environ.get("SystemRoot")
|
||||
user_root = os.environ.get("USERPROFILE")
|
||||
font_dir = os.path.join(system_root, "Fonts") if system_root else None
|
||||
user_font_dir = os.path.join(user_root, "AppData","Local","Microsoft","Windows", "Fonts") if user_root else None
|
||||
|
||||
# Default debian-based Linux & MacOS font dirs
|
||||
elif platform.system() == "Linux":
|
||||
font_dir = "/usr/share/fonts/truetype"
|
||||
user_font_dir = None
|
||||
elif platform.system() == "Darwin":
|
||||
font_dir = "/System/Library/Fonts"
|
||||
user_font_dir = None
|
||||
else:
|
||||
font_dir = None
|
||||
user_font_dir = None
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
files_list = []
|
||||
if s.font_dir and os.path.exists(s.font_dir):
|
||||
font_dir = s.font_dir
|
||||
files_list = files_list + [f for f in os.listdir(font_dir) if os.path.isfile(os.path.join(font_dir, f)) and f.lower().endswith(".ttf")]
|
||||
|
||||
if s.user_font_dir and os.path.exists(s.user_font_dir):
|
||||
files_list = files_list + [f for f in os.listdir(s.user_font_dir) if os.path.isfile(os.path.join(s.user_font_dir, f)) and f.lower().endswith(".ttf")]
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
"grid_spacing": ("INT", {"min": 0, "max": 500, "step": 5, "default": 0, }),
|
||||
"output_individuals": (["False", "True"], {"default": "False"}),
|
||||
"flip_xy": (["False", "True"], {"default": "False"}),
|
||||
},
|
||||
"optional": {
|
||||
"X": ("X_Y",),
|
||||
"Y": ("X_Y",),
|
||||
"font": (["None"] + files_list,)
|
||||
},
|
||||
"hidden": {"my_unique_id": "UNIQUE_ID"}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PIPE_LINE",)
|
||||
RETURN_NAMES = ("pipe",)
|
||||
FUNCTION = "plot"
|
||||
|
||||
CATEGORY = "EasyUse/Pipe"
|
||||
|
||||
def plot(self, pipe, grid_spacing, output_individuals, flip_xy, X=None, Y=None, font=None, my_unique_id=None):
|
||||
font_path = os.path.join(self.font_dir, font) if font != "None" else None
|
||||
if font_path and not os.path.exists(font_path):
|
||||
font_path = os.path.join(self.user_font_dir, font)
|
||||
|
||||
if X != None:
|
||||
x_axis = X.get('axis')
|
||||
x_values = X.get('values')
|
||||
else:
|
||||
x_axis = "Nothing"
|
||||
x_values = [""]
|
||||
if Y != None:
|
||||
y_axis = Y.get('axis')
|
||||
y_values = Y.get('values')
|
||||
else:
|
||||
y_axis = "Nothing"
|
||||
y_values = [""]
|
||||
|
||||
if pipe is not None:
|
||||
new_pipe = pipe
|
||||
positive = pipe["loader_settings"]["positive"] if "positive" in pipe["loader_settings"] else ""
|
||||
negative = pipe["loader_settings"]["negative"] if "negative" in pipe["loader_settings"] else ""
|
||||
|
||||
if x_axis == 'advanced: ModelMergeBlocks':
|
||||
models = X.get('models')
|
||||
vae_use = X.get('vae_use')
|
||||
if models is None:
|
||||
raise Exception("models is not found")
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"models": models,
|
||||
"vae_use": vae_use
|
||||
}
|
||||
if y_axis == 'advanced: ModelMergeBlocks':
|
||||
models = Y.get('models')
|
||||
vae_use = Y.get('vae_use')
|
||||
if models is None:
|
||||
raise Exception("models is not found")
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"models": models,
|
||||
"vae_use": vae_use
|
||||
}
|
||||
|
||||
if x_axis in ['advanced: Lora', 'advanced: Checkpoint']:
|
||||
lora_stack = X.get('lora_stack')
|
||||
_lora_stack = []
|
||||
if lora_stack is not None:
|
||||
for lora in lora_stack:
|
||||
_lora_stack.append(
|
||||
{"lora_name": lora[0], "model": pipe['model'], "clip": pipe['clip'], "model_strength": lora[1],
|
||||
"clip_strength": lora[2]})
|
||||
del lora_stack
|
||||
x_values = "; ".join(x_values)
|
||||
lora_stack = pipe['lora_stack'] + _lora_stack if 'lora_stack' in pipe else _lora_stack
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"lora_stack": lora_stack,
|
||||
}
|
||||
|
||||
if y_axis in ['advanced: Lora', 'advanced: Checkpoint']:
|
||||
lora_stack = Y.get('lora_stack')
|
||||
_lora_stack = []
|
||||
if lora_stack is not None:
|
||||
for lora in lora_stack:
|
||||
_lora_stack.append(
|
||||
{"lora_name": lora[0], "model": pipe['model'], "clip": pipe['clip'], "model_strength": lora[1],
|
||||
"clip_strength": lora[2]})
|
||||
del lora_stack
|
||||
y_values = "; ".join(y_values)
|
||||
lora_stack = pipe['lora_stack'] + _lora_stack if 'lora_stack' in pipe else _lora_stack
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"lora_stack": lora_stack,
|
||||
}
|
||||
|
||||
if x_axis == 'advanced: Seeds++ Batch':
|
||||
if new_pipe['seed']:
|
||||
value = x_values
|
||||
x_values = []
|
||||
for index in range(value):
|
||||
x_values.append(str(new_pipe['seed'] + index))
|
||||
x_values = "; ".join(x_values)
|
||||
if y_axis == 'advanced: Seeds++ Batch':
|
||||
if new_pipe['seed']:
|
||||
value = y_values
|
||||
y_values = []
|
||||
for index in range(value):
|
||||
y_values.append(str(new_pipe['seed'] + index))
|
||||
y_values = "; ".join(y_values)
|
||||
|
||||
if x_axis == 'advanced: Positive Prompt S/R':
|
||||
if positive:
|
||||
x_value = x_values
|
||||
x_values = []
|
||||
for index, value in enumerate(x_value):
|
||||
search_txt, replace_txt, replace_all = value
|
||||
if replace_all:
|
||||
txt = replace_txt if replace_txt is not None else positive
|
||||
x_values.append(txt)
|
||||
else:
|
||||
txt = positive.replace(search_txt, replace_txt, 1) if replace_txt is not None else positive
|
||||
x_values.append(txt)
|
||||
x_values = "; ".join(x_values)
|
||||
if y_axis == 'advanced: Positive Prompt S/R':
|
||||
if positive:
|
||||
y_value = y_values
|
||||
y_values = []
|
||||
for index, value in enumerate(y_value):
|
||||
search_txt, replace_txt, replace_all = value
|
||||
if replace_all:
|
||||
txt = replace_txt if replace_txt is not None else positive
|
||||
y_values.append(txt)
|
||||
else:
|
||||
txt = positive.replace(search_txt, replace_txt, 1) if replace_txt is not None else positive
|
||||
y_values.append(txt)
|
||||
y_values = "; ".join(y_values)
|
||||
|
||||
if x_axis == 'advanced: Negative Prompt S/R':
|
||||
if negative:
|
||||
x_value = x_values
|
||||
x_values = []
|
||||
for index, value in enumerate(x_value):
|
||||
search_txt, replace_txt, replace_all = value
|
||||
if replace_all:
|
||||
txt = replace_txt if replace_txt is not None else negative
|
||||
x_values.append(txt)
|
||||
else:
|
||||
txt = negative.replace(search_txt, replace_txt, 1) if replace_txt is not None else negative
|
||||
x_values.append(txt)
|
||||
x_values = "; ".join(x_values)
|
||||
if y_axis == 'advanced: Negative Prompt S/R':
|
||||
if negative:
|
||||
y_value = y_values
|
||||
y_values = []
|
||||
for index, value in enumerate(y_value):
|
||||
search_txt, replace_txt, replace_all = value
|
||||
if replace_all:
|
||||
txt = replace_txt if replace_txt is not None else negative
|
||||
y_values.append(txt)
|
||||
else:
|
||||
txt = negative.replace(search_txt, replace_txt, 1) if replace_txt is not None else negative
|
||||
y_values.append(txt)
|
||||
y_values = "; ".join(y_values)
|
||||
|
||||
if "advanced: ControlNet" in x_axis:
|
||||
x_value = x_values
|
||||
x_values = []
|
||||
cnet = []
|
||||
for index, value in enumerate(x_value):
|
||||
cnet.append(value)
|
||||
x_values.append(str(index))
|
||||
x_values = "; ".join(x_values)
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"cnet_stack": cnet,
|
||||
}
|
||||
|
||||
if "advanced: ControlNet" in y_axis:
|
||||
y_value = y_values
|
||||
y_values = []
|
||||
cnet = []
|
||||
for index, value in enumerate(y_value):
|
||||
cnet.append(value)
|
||||
y_values.append(str(index))
|
||||
y_values = "; ".join(y_values)
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"cnet_stack": cnet,
|
||||
}
|
||||
|
||||
if "advanced: Pos Condition" in x_axis:
|
||||
x_values = "; ".join(x_values)
|
||||
cond = X.get('cond')
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"positive_cond_stack": cond,
|
||||
}
|
||||
if "advanced: Pos Condition" in y_axis:
|
||||
y_values = "; ".join(y_values)
|
||||
cond = Y.get('cond')
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"positive_cond_stack": cond,
|
||||
}
|
||||
|
||||
if "advanced: Neg Condition" in x_axis:
|
||||
x_values = "; ".join(x_values)
|
||||
cond = X.get('cond')
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"negative_cond_stack": cond,
|
||||
}
|
||||
if "advanced: Neg Condition" in y_axis:
|
||||
y_values = "; ".join(y_values)
|
||||
cond = Y.get('cond')
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"negative_cond_stack": cond,
|
||||
}
|
||||
|
||||
del pipe
|
||||
|
||||
return pipeXYPlot().plot(grid_spacing, output_individuals, flip_xy, x_axis, x_values, y_axis, y_values, new_pipe, font_path)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"easy pipeIn": pipeIn,
|
||||
"easy pipeOut": pipeOut,
|
||||
"easy pipeEdit": pipeEdit,
|
||||
"easy pipeEditPrompt": pipeEditPrompt,
|
||||
"easy pipeToBasicPipe": pipeToBasicPipe,
|
||||
"easy pipeBatchIndex": pipeBatchIndex,
|
||||
"easy XYPlot": pipeXYPlot,
|
||||
"easy XYPlotAdvanced": pipeXYPlotAdvanced
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy pipeIn": "Pipe In",
|
||||
"easy pipeOut": "Pipe Out",
|
||||
"easy pipeEdit": "Pipe Edit",
|
||||
"easy pipeEditPrompt": "Pipe Edit Prompt",
|
||||
"easy pipeBatchIndex": "Pipe Batch Index",
|
||||
"easy pipeToBasicPipe": "Pipe -> BasicPipe",
|
||||
"easy XYPlot": "XY Plot",
|
||||
"easy XYPlotAdvanced": "XY Plot Advanced"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,541 @@
|
||||
import os
|
||||
import json
|
||||
import folder_paths
|
||||
from urllib.request import urlopen
|
||||
from ..libs.log import log_node_info
|
||||
from ..libs.wildcards import get_wildcard_list, process
|
||||
from ..libs.utils import AlwaysEqualProxy
|
||||
from ..config import RESOURCES_DIR, FOOOCUS_STYLES_DIR, MAX_SEED_NUM, PROMPT_TEMPLATE
|
||||
from .. import easyCache
|
||||
|
||||
# 正面提示词
|
||||
class positivePrompt:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"positive": ("STRING", {"default": "", "multiline": True, "placeholder": "Positive"}),}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("positive",)
|
||||
FUNCTION = "main"
|
||||
|
||||
CATEGORY = "EasyUse/Prompt"
|
||||
|
||||
@staticmethod
|
||||
def main(positive):
|
||||
return positive,
|
||||
|
||||
# 通配符提示词
|
||||
class wildcardsPrompt:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
wildcard_list = get_wildcard_list()
|
||||
return {"required": {
|
||||
"text": ("STRING", {"default": "", "multiline": True, "dynamicPrompts": False, "placeholder": "(Support Lora Block Weight and wildcard)"}),
|
||||
"Select to add LoRA": (["Select the LoRA to add to the text"] + folder_paths.get_filename_list("loras"),),
|
||||
"Select to add Wildcard": (["Select the Wildcard to add to the text"] + wildcard_list,),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": MAX_SEED_NUM}),
|
||||
"multiline_mode": ("BOOLEAN", {"default": False}),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING")
|
||||
RETURN_NAMES = ("text", "populated_text")
|
||||
OUTPUT_IS_LIST = (True, True)
|
||||
FUNCTION = "main"
|
||||
|
||||
CATEGORY = "EasyUse/Prompt"
|
||||
|
||||
def translate(self, text):
|
||||
return text
|
||||
|
||||
def main(self, *args, **kwargs):
|
||||
prompt = kwargs["prompt"] if "prompt" in kwargs else None
|
||||
seed = kwargs["seed"]
|
||||
|
||||
# Clean loaded_objects
|
||||
if prompt:
|
||||
easyCache.update_loaded_objects(prompt)
|
||||
|
||||
text = kwargs['text']
|
||||
if "multiline_mode" in kwargs and kwargs["multiline_mode"]:
|
||||
populated_text = []
|
||||
_text = []
|
||||
text = text.split("\n")
|
||||
for t in text:
|
||||
t = self.translate(t)
|
||||
_text.append(t)
|
||||
populated_text.append(process(t, seed))
|
||||
text = _text
|
||||
else:
|
||||
text = self.translate(text)
|
||||
populated_text = [process(text, seed)]
|
||||
text = [text]
|
||||
return {"ui": {"value": [seed]}, "result": (text, populated_text)}
|
||||
|
||||
# 负面提示词
|
||||
class negativePrompt:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"negative": ("STRING", {"default": "", "multiline": True, "placeholder": "Negative"}),}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("negative",)
|
||||
FUNCTION = "main"
|
||||
|
||||
CATEGORY = "EasyUse/Prompt"
|
||||
|
||||
@staticmethod
|
||||
def main(negative):
|
||||
return negative,
|
||||
|
||||
# 风格提示词选择器
|
||||
class stylesPromptSelector:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
styles = ["fooocus_styles"]
|
||||
styles_dir = FOOOCUS_STYLES_DIR
|
||||
for file_name in os.listdir(styles_dir):
|
||||
file = os.path.join(styles_dir, file_name)
|
||||
if os.path.isfile(file) and file_name.endswith(".json"):
|
||||
styles.append(file_name.split(".")[0])
|
||||
return {
|
||||
"required": {
|
||||
"styles": (styles, {"default": "fooocus_styles"}),
|
||||
},
|
||||
"optional": {
|
||||
"positive": ("STRING", {"forceInput": True}),
|
||||
"negative": ("STRING", {"forceInput": True}),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING",)
|
||||
RETURN_NAMES = ("positive", "negative",)
|
||||
|
||||
CATEGORY = 'EasyUse/Prompt'
|
||||
FUNCTION = 'run'
|
||||
|
||||
def run(self, styles, positive='', negative='', prompt=None, extra_pnginfo=None, my_unique_id=None):
|
||||
values = []
|
||||
all_styles = {}
|
||||
positive_prompt, negative_prompt = '', negative
|
||||
if styles == "fooocus_styles":
|
||||
file = os.path.join(RESOURCES_DIR, styles + '.json')
|
||||
else:
|
||||
file = os.path.join(FOOOCUS_STYLES_DIR, styles + '.json')
|
||||
f = open(file, 'r', encoding='utf-8')
|
||||
data = json.load(f)
|
||||
f.close()
|
||||
for d in data:
|
||||
all_styles[d['name']] = d
|
||||
if my_unique_id in prompt:
|
||||
if prompt[my_unique_id]["inputs"]['select_styles']:
|
||||
values = prompt[my_unique_id]["inputs"]['select_styles'].split(',')
|
||||
|
||||
has_prompt = False
|
||||
if len(values) == 0:
|
||||
return (positive, negative)
|
||||
|
||||
for index, val in enumerate(values):
|
||||
if 'prompt' in all_styles[val]:
|
||||
if "{prompt}" in all_styles[val]['prompt'] and has_prompt == False:
|
||||
positive_prompt = all_styles[val]['prompt'].replace('{prompt}', positive)
|
||||
has_prompt = True
|
||||
else:
|
||||
positive_prompt += ', ' + all_styles[val]['prompt'].replace(', {prompt}', '').replace('{prompt}', '')
|
||||
if 'negative_prompt' in all_styles[val]:
|
||||
negative_prompt += ', ' + all_styles[val]['negative_prompt'] if negative_prompt else all_styles[val]['negative_prompt']
|
||||
|
||||
if has_prompt == False and positive:
|
||||
positive_prompt = positive + ', '
|
||||
|
||||
return (positive_prompt, negative_prompt)
|
||||
|
||||
#prompt
|
||||
class prompt:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"text": ("STRING", {"default": "", "multiline": True, "placeholder": "Prompt"}),
|
||||
"prefix": (["Select the prefix add to the text"] + PROMPT_TEMPLATE["prefix"], {"default": "Select the prefix add to the text"}),
|
||||
"subject": (["👤Select the subject add to the text"] + PROMPT_TEMPLATE["subject"], {"default": "👤Select the subject add to the text"}),
|
||||
"action": (["🎬Select the action add to the text"] + PROMPT_TEMPLATE["action"], {"default": "🎬Select the action add to the text"}),
|
||||
"clothes": (["👚Select the clothes add to the text"] + PROMPT_TEMPLATE["clothes"], {"default": "👚Select the clothes add to the text"}),
|
||||
"environment": (["☀️Select the illumination environment add to the text"] + PROMPT_TEMPLATE["environment"], {"default": "☀️Select the illumination environment add to the text"}),
|
||||
"background": (["🎞️Select the background add to the text"] + PROMPT_TEMPLATE["background"], {"default": "🎞️Select the background add to the text"}),
|
||||
"nsfw": (["🔞Select the nsfw add to the text"] + PROMPT_TEMPLATE["nsfw"], {"default": "🔞️Select the nsfw add to the text"}),
|
||||
},"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID"},}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompt",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "EasyUse/Prompt"
|
||||
|
||||
def doit(self, *args, **kwargs):
|
||||
text = kwargs['text']
|
||||
return (text,)
|
||||
|
||||
#promptList
|
||||
class promptList:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {
|
||||
"prompt_1": ("STRING", {"multiline": True, "default": ""}),
|
||||
"prompt_2": ("STRING", {"multiline": True, "default": ""}),
|
||||
"prompt_3": ("STRING", {"multiline": True, "default": ""}),
|
||||
"prompt_4": ("STRING", {"multiline": True, "default": ""}),
|
||||
"prompt_5": ("STRING", {"multiline": True, "default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"optional_prompt_list": ("LIST",)
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("LIST", "STRING")
|
||||
RETURN_NAMES = ("prompt_list", "prompt_strings")
|
||||
OUTPUT_IS_LIST = (False, True)
|
||||
FUNCTION = "run"
|
||||
CATEGORY = "EasyUse/Prompt"
|
||||
|
||||
def run(self, **kwargs):
|
||||
prompts = []
|
||||
|
||||
if "optional_prompt_list" in kwargs:
|
||||
for l in kwargs["optional_prompt_list"]:
|
||||
prompts.append(l)
|
||||
|
||||
# Iterate over the received inputs in sorted order.
|
||||
for k in sorted(kwargs.keys()):
|
||||
v = kwargs[k]
|
||||
|
||||
# Only process string input ports.
|
||||
if isinstance(v, str) and v != '':
|
||||
prompts.append(v)
|
||||
|
||||
return (prompts, prompts)
|
||||
|
||||
#promptLine
|
||||
class promptLine:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"prompt": ("STRING", {"multiline": True, "default": "text"}),
|
||||
"start_index": ("INT", {"default": 0, "min": 0, "max": 9999}),
|
||||
"max_rows": ("INT", {"default": 1000, "min": 1, "max": 9999}),
|
||||
},
|
||||
"hidden":{
|
||||
"workflow_prompt": "PROMPT", "my_unique_id": "UNIQUE_ID"
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", AlwaysEqualProxy('*'))
|
||||
RETURN_NAMES = ("STRING", "COMBO")
|
||||
OUTPUT_IS_LIST = (True, True)
|
||||
FUNCTION = "generate_strings"
|
||||
CATEGORY = "EasyUse/Prompt"
|
||||
|
||||
def generate_strings(self, prompt, start_index, max_rows, workflow_prompt=None, my_unique_id=None):
|
||||
lines = prompt.split('\n')
|
||||
# lines = [zh_to_en([v])[0] if has_chinese(v) else v for v in lines if v]
|
||||
|
||||
start_index = max(0, min(start_index, len(lines) - 1))
|
||||
|
||||
end_index = min(start_index + max_rows, len(lines))
|
||||
|
||||
rows = lines[start_index:end_index]
|
||||
|
||||
return (rows, rows)
|
||||
|
||||
class promptConcat:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {
|
||||
},
|
||||
"optional": {
|
||||
"prompt1": ("STRING", {"multiline": False, "default": "", "forceInput": True}),
|
||||
"prompt2": ("STRING", {"multiline": False, "default": "", "forceInput": True}),
|
||||
"separator": ("STRING", {"multiline": False, "default": ""}),
|
||||
},
|
||||
}
|
||||
RETURN_TYPES = ("STRING", )
|
||||
RETURN_NAMES = ("prompt", )
|
||||
FUNCTION = "concat_text"
|
||||
CATEGORY = "EasyUse/Prompt"
|
||||
|
||||
def concat_text(self, prompt1="", prompt2="", separator=""):
|
||||
|
||||
return (prompt1 + separator + prompt2,)
|
||||
|
||||
class promptReplace:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"prompt": ("STRING", {"multiline": True, "default": "", "forceInput": True}),
|
||||
},
|
||||
"optional": {
|
||||
"find1": ("STRING", {"multiline": False, "default": ""}),
|
||||
"replace1": ("STRING", {"multiline": False, "default": ""}),
|
||||
"find2": ("STRING", {"multiline": False, "default": ""}),
|
||||
"replace2": ("STRING", {"multiline": False, "default": ""}),
|
||||
"find3": ("STRING", {"multiline": False, "default": ""}),
|
||||
"replace3": ("STRING", {"multiline": False, "default": ""}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompt",)
|
||||
FUNCTION = "replace_text"
|
||||
CATEGORY = "EasyUse/Prompt"
|
||||
|
||||
def replace_text(self, prompt, find1="", replace1="", find2="", replace2="", find3="", replace3=""):
|
||||
|
||||
prompt = prompt.replace(find1, replace1)
|
||||
prompt = prompt.replace(find2, replace2)
|
||||
prompt = prompt.replace(find3, replace3)
|
||||
|
||||
return (prompt,)
|
||||
|
||||
|
||||
# 肖像大师
|
||||
# Created by AI Wiz Art (Stefano Flore)
|
||||
# Version: 2.2
|
||||
# https://stefanoflore.it
|
||||
# https://ai-wiz.art
|
||||
class portraitMaster:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
max_float_value = 1.95
|
||||
prompt_path = os.path.join(RESOURCES_DIR, 'portrait_prompt.json')
|
||||
if not os.path.exists(prompt_path):
|
||||
response = urlopen('https://raw.githubusercontent.com/yolain/ComfyUI-Easy-Use/main/resources/portrait_prompt.json')
|
||||
temp_prompt = json.loads(response.read())
|
||||
prompt_serialized = json.dumps(temp_prompt, indent=4)
|
||||
with open(prompt_path, "w") as f:
|
||||
f.write(prompt_serialized)
|
||||
del response, temp_prompt
|
||||
# Load local
|
||||
with open(prompt_path, 'r') as f:
|
||||
list = json.load(f)
|
||||
keys = [
|
||||
['shot', 'COMBO', {"key": "shot_list"}], ['shot_weight', 'FLOAT'],
|
||||
['gender', 'COMBO', {"default": "Woman", "key": "gender_list"}], ['age', 'INT', {"default": 30, "min": 18, "max": 90, "step": 1, "display": "slider"}],
|
||||
['nationality_1', 'COMBO', {"default": "Chinese", "key": "nationality_list"}], ['nationality_2', 'COMBO', {"key": "nationality_list"}], ['nationality_mix', 'FLOAT'],
|
||||
['body_type', 'COMBO', {"key": "body_type_list"}], ['body_type_weight', 'FLOAT'], ['model_pose', 'COMBO', {"key": "model_pose_list"}], ['eyes_color', 'COMBO', {"key": "eyes_color_list"}],
|
||||
['facial_expression', 'COMBO', {"key": "face_expression_list"}], ['facial_expression_weight', 'FLOAT'], ['face_shape', 'COMBO', {"key": "face_shape_list"}], ['face_shape_weight', 'FLOAT'], ['facial_asymmetry', 'FLOAT'],
|
||||
['hair_style', 'COMBO', {"key": "hair_style_list"}], ['hair_color', 'COMBO', {"key": "hair_color_list"}], ['disheveled', 'FLOAT'], ['beard', 'COMBO', {"key": "beard_list"}],
|
||||
['skin_details', 'FLOAT'], ['skin_pores', 'FLOAT'], ['dimples', 'FLOAT'], ['freckles', 'FLOAT'],
|
||||
['moles', 'FLOAT'], ['skin_imperfections', 'FLOAT'], ['skin_acne', 'FLOAT'], ['tanned_skin', 'FLOAT'],
|
||||
['eyes_details', 'FLOAT'], ['iris_details', 'FLOAT'], ['circular_iris', 'FLOAT'], ['circular_pupil', 'FLOAT'],
|
||||
['light_type', 'COMBO', {"key": "light_type_list"}], ['light_direction', 'COMBO', {"key": "light_direction_list"}], ['light_weight', 'FLOAT']
|
||||
]
|
||||
widgets = {}
|
||||
for i, obj in enumerate(keys):
|
||||
if obj[1] == 'COMBO':
|
||||
key = obj[2]['key'] if obj[2] and 'key' in obj[2] else obj[0]
|
||||
_list = list[key].copy()
|
||||
_list.insert(0, '-')
|
||||
widgets[obj[0]] = (_list, {**obj[2]})
|
||||
elif obj[1] == 'FLOAT':
|
||||
widgets[obj[0]] = ("FLOAT", {"default": 0, "step": 0.05, "min": 0, "max": max_float_value, "display": "slider",})
|
||||
elif obj[1] == 'INT':
|
||||
widgets[obj[0]] = (obj[1], obj[2])
|
||||
del list
|
||||
return {
|
||||
"required": {
|
||||
**widgets,
|
||||
"photorealism_improvement": (["enable", "disable"],),
|
||||
"prompt_start": ("STRING", {"multiline": True, "default": "raw photo, (realistic:1.5)"}),
|
||||
"prompt_additional": ("STRING", {"multiline": True, "default": ""}),
|
||||
"prompt_end": ("STRING", {"multiline": True, "default": ""}),
|
||||
"negative_prompt": ("STRING", {"multiline": True, "default": ""}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING",)
|
||||
RETURN_NAMES = ("positive", "negative",)
|
||||
|
||||
FUNCTION = "pm"
|
||||
|
||||
CATEGORY = "EasyUse/Prompt"
|
||||
|
||||
def pm(self, shot="-", shot_weight=1, gender="-", body_type="-", body_type_weight=0, eyes_color="-",
|
||||
facial_expression="-", facial_expression_weight=0, face_shape="-", face_shape_weight=0,
|
||||
nationality_1="-", nationality_2="-", nationality_mix=0.5, age=30, hair_style="-", hair_color="-",
|
||||
disheveled=0, dimples=0, freckles=0, skin_pores=0, skin_details=0, moles=0, skin_imperfections=0,
|
||||
wrinkles=0, tanned_skin=0, eyes_details=1, iris_details=1, circular_iris=1, circular_pupil=1,
|
||||
facial_asymmetry=0, prompt_additional="", prompt_start="", prompt_end="", light_type="-",
|
||||
light_direction="-", light_weight=0, negative_prompt="", photorealism_improvement="disable", beard="-",
|
||||
model_pose="-", skin_acne=0):
|
||||
|
||||
prompt = []
|
||||
|
||||
if gender == "-":
|
||||
gender = ""
|
||||
else:
|
||||
if age <= 25 and gender == 'Woman':
|
||||
gender = 'girl'
|
||||
if age <= 25 and gender == 'Man':
|
||||
gender = 'boy'
|
||||
gender = " " + gender + " "
|
||||
|
||||
if nationality_1 != '-' and nationality_2 != '-':
|
||||
nationality = f"[{nationality_1}:{nationality_2}:{round(nationality_mix, 2)}]"
|
||||
elif nationality_1 != '-':
|
||||
nationality = nationality_1 + " "
|
||||
elif nationality_2 != '-':
|
||||
nationality = nationality_2 + " "
|
||||
else:
|
||||
nationality = ""
|
||||
|
||||
if prompt_start != "":
|
||||
prompt.append(f"{prompt_start}")
|
||||
|
||||
if shot != "-" and shot_weight > 0:
|
||||
prompt.append(f"({shot}:{round(shot_weight, 2)})")
|
||||
|
||||
prompt.append(f"({nationality}{gender}{round(age)}-years-old:1.5)")
|
||||
|
||||
if body_type != "-" and body_type_weight > 0:
|
||||
prompt.append(f"({body_type}, {body_type} body:{round(body_type_weight, 2)})")
|
||||
|
||||
if model_pose != "-":
|
||||
prompt.append(f"({model_pose}:1.5)")
|
||||
|
||||
if eyes_color != "-":
|
||||
prompt.append(f"({eyes_color} eyes:1.25)")
|
||||
|
||||
if facial_expression != "-" and facial_expression_weight > 0:
|
||||
prompt.append(
|
||||
f"({facial_expression}, {facial_expression} expression:{round(facial_expression_weight, 2)})")
|
||||
|
||||
if face_shape != "-" and face_shape_weight > 0:
|
||||
prompt.append(f"({face_shape} shape face:{round(face_shape_weight, 2)})")
|
||||
|
||||
if hair_style != "-":
|
||||
prompt.append(f"({hair_style} hairstyle:1.25)")
|
||||
|
||||
if hair_color != "-":
|
||||
prompt.append(f"({hair_color} hair:1.25)")
|
||||
|
||||
if beard != "-":
|
||||
prompt.append(f"({beard}:1.15)")
|
||||
|
||||
if disheveled != "-" and disheveled > 0:
|
||||
prompt.append(f"(disheveled:{round(disheveled, 2)})")
|
||||
|
||||
if prompt_additional != "":
|
||||
prompt.append(f"{prompt_additional}")
|
||||
|
||||
if skin_details > 0:
|
||||
prompt.append(f"(skin details, skin texture:{round(skin_details, 2)})")
|
||||
|
||||
if skin_pores > 0:
|
||||
prompt.append(f"(skin pores:{round(skin_pores, 2)})")
|
||||
|
||||
if skin_imperfections > 0:
|
||||
prompt.append(f"(skin imperfections:{round(skin_imperfections, 2)})")
|
||||
|
||||
if skin_acne > 0:
|
||||
prompt.append(f"(acne, skin with acne:{round(skin_acne, 2)})")
|
||||
|
||||
if wrinkles > 0:
|
||||
prompt.append(f"(skin imperfections:{round(wrinkles, 2)})")
|
||||
|
||||
if tanned_skin > 0:
|
||||
prompt.append(f"(tanned skin:{round(tanned_skin, 2)})")
|
||||
|
||||
if dimples > 0:
|
||||
prompt.append(f"(dimples:{round(dimples, 2)})")
|
||||
|
||||
if freckles > 0:
|
||||
prompt.append(f"(freckles:{round(freckles, 2)})")
|
||||
|
||||
if moles > 0:
|
||||
prompt.append(f"(skin pores:{round(moles, 2)})")
|
||||
|
||||
if eyes_details > 0:
|
||||
prompt.append(f"(eyes details:{round(eyes_details, 2)})")
|
||||
|
||||
if iris_details > 0:
|
||||
prompt.append(f"(iris details:{round(iris_details, 2)})")
|
||||
|
||||
if circular_iris > 0:
|
||||
prompt.append(f"(circular iris:{round(circular_iris, 2)})")
|
||||
|
||||
if circular_pupil > 0:
|
||||
prompt.append(f"(circular pupil:{round(circular_pupil, 2)})")
|
||||
|
||||
if facial_asymmetry > 0:
|
||||
prompt.append(f"(facial asymmetry, face asymmetry:{round(facial_asymmetry, 2)})")
|
||||
|
||||
if light_type != '-' and light_weight > 0:
|
||||
if light_direction != '-':
|
||||
prompt.append(f"({light_type} {light_direction}:{round(light_weight, 2)})")
|
||||
else:
|
||||
prompt.append(f"({light_type}:{round(light_weight, 2)})")
|
||||
|
||||
if prompt_end != "":
|
||||
prompt.append(f"{prompt_end}")
|
||||
|
||||
prompt = ", ".join(prompt)
|
||||
prompt = prompt.lower()
|
||||
|
||||
if photorealism_improvement == "enable":
|
||||
prompt = prompt + ", (professional photo, balanced photo, balanced exposure:1.2), (film grain:1.15)"
|
||||
|
||||
if photorealism_improvement == "enable":
|
||||
negative_prompt = negative_prompt + ", (shinny skin, reflections on the skin, skin reflections:1.25)"
|
||||
|
||||
log_node_info("Portrait Master as generate the prompt:", prompt)
|
||||
|
||||
return (prompt, negative_prompt,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"easy positive": positivePrompt,
|
||||
"easy negative": negativePrompt,
|
||||
"easy wildcards": wildcardsPrompt,
|
||||
"easy prompt": prompt,
|
||||
"easy promptList": promptList,
|
||||
"easy promptLine": promptLine,
|
||||
"easy promptConcat": promptConcat,
|
||||
"easy promptReplace": promptReplace,
|
||||
"easy stylesSelector": stylesPromptSelector,
|
||||
"easy portraitMaster": portraitMaster,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy positive": "Positive",
|
||||
"easy negative": "Negative",
|
||||
"easy wildcards": "Wildcards",
|
||||
"easy prompt": "Prompt",
|
||||
"easy promptList": "PromptList",
|
||||
"easy promptLine": "PromptLine",
|
||||
"easy promptConcat": "PromptConcat",
|
||||
"easy promptReplace": "PromptReplace",
|
||||
"easy stylesSelector": "Styles Selector",
|
||||
"easy portraitMaster": "Portrait Master",
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
||||
from ..config import MAX_SEED_NUM
|
||||
|
||||
class easySeed:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": MAX_SEED_NUM}),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
RETURN_NAMES = ("seed",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "EasyUse/Seed"
|
||||
|
||||
def doit(self, seed=0, prompt=None, extra_pnginfo=None, my_unique_id=None):
|
||||
return seed,
|
||||
|
||||
# 全局随机种
|
||||
class globalSeed:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("INT", {"default": 0, "min": 0, "max": MAX_SEED_NUM}),
|
||||
"mode": ("BOOLEAN", {"default": True, "label_on": "control_before_generate", "label_off": "control_after_generate"}),
|
||||
"action": (["fixed", "increment", "decrement", "randomize",
|
||||
"increment for each node", "decrement for each node", "randomize for each node"], ),
|
||||
"last_seed": ("STRING", {"default": ""}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "EasyUse/Seed"
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def doit(self, **kwargs):
|
||||
return {}
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"easy seed": easySeed,
|
||||
"easy globalSeed": globalSeed,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy seed": "EasySeed",
|
||||
"easy globalSeed": "EasyGlobalSeed",
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import os
|
||||
import folder_paths
|
||||
from ..libs.utils import AlwaysEqualProxy
|
||||
|
||||
class showLoaderSettingsNames:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"pipe": ("PIPE_LINE",),
|
||||
},
|
||||
"hidden": {
|
||||
"unique_id": "UNIQUE_ID",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "STRING",)
|
||||
RETURN_NAMES = ("ckpt_name", "vae_name", "lora_name")
|
||||
|
||||
FUNCTION = "notify"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
CATEGORY = "EasyUse/Util"
|
||||
|
||||
def notify(self, pipe, names=None, unique_id=None, extra_pnginfo=None):
|
||||
if unique_id and extra_pnginfo and "workflow" in extra_pnginfo:
|
||||
workflow = extra_pnginfo["workflow"]
|
||||
node = next((x for x in workflow["nodes"] if str(x["id"]) == unique_id), None)
|
||||
if node:
|
||||
ckpt_name = pipe['loader_settings']['ckpt_name'] if 'ckpt_name' in pipe['loader_settings'] else ''
|
||||
vae_name = pipe['loader_settings']['vae_name'] if 'vae_name' in pipe['loader_settings'] else ''
|
||||
lora_name = pipe['loader_settings']['lora_name'] if 'lora_name' in pipe['loader_settings'] else ''
|
||||
|
||||
if ckpt_name:
|
||||
ckpt_name = os.path.basename(os.path.splitext(ckpt_name)[0])
|
||||
if vae_name:
|
||||
vae_name = os.path.basename(os.path.splitext(vae_name)[0])
|
||||
if lora_name:
|
||||
lora_name = os.path.basename(os.path.splitext(lora_name)[0])
|
||||
|
||||
names = "ckpt_name: " + ckpt_name + '\n' + "vae_name: " + vae_name + '\n' + "lora_name: " + lora_name
|
||||
node["widgets_values"] = names
|
||||
|
||||
return {"ui": {"text": [names]}, "result": (ckpt_name, vae_name, lora_name)}
|
||||
|
||||
class sliderControl:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"mode": (['ipadapter layer weights'],),
|
||||
"model_type": (['sdxl', 'sd1'],),
|
||||
},
|
||||
"hidden": {
|
||||
"prompt": "PROMPT",
|
||||
"my_unique_id": "UNIQUE_ID",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("layer_weights",)
|
||||
|
||||
FUNCTION = "control"
|
||||
|
||||
CATEGORY = "EasyUse/Util"
|
||||
|
||||
def control(self, mode, model_type, prompt=None, my_unique_id=None, extra_pnginfo=None):
|
||||
values = ''
|
||||
if my_unique_id in prompt:
|
||||
if 'values' in prompt[my_unique_id]["inputs"]:
|
||||
values = prompt[my_unique_id]["inputs"]['values']
|
||||
|
||||
return (values,)
|
||||
|
||||
class setCkptName:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {
|
||||
"ckpt_name": (folder_paths.get_filename_list("checkpoints"),),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (AlwaysEqualProxy('*'),)
|
||||
RETURN_NAMES = ("ckpt_name",)
|
||||
FUNCTION = "set_name"
|
||||
CATEGORY = "EasyUse/Util"
|
||||
|
||||
def set_name(self, ckpt_name):
|
||||
return (ckpt_name,)
|
||||
|
||||
class setControlName:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {
|
||||
"controlnet_name": (folder_paths.get_filename_list("controlnet"),),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (AlwaysEqualProxy('*'),)
|
||||
RETURN_NAMES = ("controlnet_name",)
|
||||
FUNCTION = "set_name"
|
||||
CATEGORY = "EasyUse/Util"
|
||||
|
||||
def set_name(self, controlnet_name):
|
||||
return (controlnet_name,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"easy showLoaderSettingsNames": showLoaderSettingsNames,
|
||||
"easy sliderControl": sliderControl,
|
||||
"easy ckptNames": setCkptName,
|
||||
"easy controlnetNames": setControlName,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy showLoaderSettingsNames": "Show Loader Settings Names",
|
||||
"easy sliderControl": "Easy Slider Control",
|
||||
"easy ckptNames": "Ckpt Names",
|
||||
"easy controlnetNames": "ControlNet Names",
|
||||
}
|
||||
@@ -2,8 +2,8 @@ import os
|
||||
import json
|
||||
import comfy
|
||||
import folder_paths
|
||||
from .config import RESOURCES_DIR
|
||||
from .libs.utils import getMetadata
|
||||
from ..config import RESOURCES_DIR
|
||||
from ..libs.utils import getMetadata
|
||||
def load_preset(filename):
|
||||
path = os.path.join(RESOURCES_DIR, filename)
|
||||
path = os.path.abspath(path)
|
||||
@@ -656,4 +656,41 @@ class XYplot_ModelMergeBlocks:
|
||||
models = (ckpt_name_1, ckpt_name_2)
|
||||
|
||||
xy_values = {"axis":axis, "values":values, "models":models, "vae_use": vae_use}
|
||||
return (xy_values,)
|
||||
return (xy_values,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"easy XYInputs: Seeds++ Batch": XYplot_SeedsBatch,
|
||||
"easy XYInputs: Steps": XYplot_Steps,
|
||||
"easy XYInputs: CFG Scale": XYplot_CFG,
|
||||
"easy XYInputs: FluxGuidance": XYplot_FluxGuidance,
|
||||
"easy XYInputs: Sampler/Scheduler": XYplot_Sampler_Scheduler,
|
||||
"easy XYInputs: Denoise": XYplot_Denoise,
|
||||
"easy XYInputs: Checkpoint": XYplot_Checkpoint,
|
||||
"easy XYInputs: Lora": XYplot_Lora,
|
||||
"easy XYInputs: ModelMergeBlocks": XYplot_ModelMergeBlocks,
|
||||
"easy XYInputs: PromptSR": XYplot_PromptSR,
|
||||
"easy XYInputs: ControlNet": XYplot_Control_Net,
|
||||
"easy XYInputs: PositiveCond": XYplot_Positive_Cond,
|
||||
"easy XYInputs: PositiveCondList": XYplot_Positive_Cond_List,
|
||||
"easy XYInputs: NegativeCond": XYplot_Negative_Cond,
|
||||
"easy XYInputs: NegativeCondList": XYplot_Negative_Cond_List,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy XYInputs: Seeds++ Batch": "XY Inputs: Seeds++ Batch //EasyUse",
|
||||
"easy XYInputs: Steps": "XY Inputs: Steps //EasyUse",
|
||||
"easy XYInputs: CFG Scale": "XY Inputs: CFG Scale //EasyUse",
|
||||
"easy XYInputs: FluxGuidance": "XY Inputs: Flux Guidance //EasyUse",
|
||||
"easy XYInputs: Sampler/Scheduler": "XY Inputs: Sampler/Scheduler //EasyUse",
|
||||
"easy XYInputs: Denoise": "XY Inputs: Denoise //EasyUse",
|
||||
"easy XYInputs: Checkpoint": "XY Inputs: Checkpoint //EasyUse",
|
||||
"easy XYInputs: Lora": "XY Inputs: Lora //EasyUse",
|
||||
"easy XYInputs: ModelMergeBlocks": "XY Inputs: ModelMergeBlocks //EasyUse",
|
||||
"easy XYInputs: PromptSR": "XY Inputs: PromptSR //EasyUse",
|
||||
"easy XYInputs: ControlNet": "XY Inputs: Controlnet //EasyUse",
|
||||
"easy XYInputs: PositiveCond": "XY Inputs: PosCond //EasyUse",
|
||||
"easy XYInputs: PositiveCondList": "XY Inputs: PosCondList //EasyUse",
|
||||
"easy XYInputs: NegativeCond": "XY Inputs: NegCond //EasyUse",
|
||||
"easy XYInputs: NegativeCondList": "XY Inputs: NegCondList //EasyUse",
|
||||
}
|
||||
+1
-5
@@ -163,8 +163,4 @@ def onprompt(json_data):
|
||||
|
||||
return json_data
|
||||
|
||||
server.PromptServer.instance.add_on_prompt_handler(onprompt)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
server.PromptServer.instance.add_on_prompt_handler(onprompt)
|
||||
Reference in New Issue
Block a user