Compare commits

..
3 Commits
Author SHA1 Message Date
Dr.Lt.Data 84fbb3fa2e fix: proper error message for outdated ComfyUI instead of import fail
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/722
2024-08-27 01:03:12 +09:00
Dr.Lt.Data b2c7e06e79 improve: ControlBridge - add Stop behavior
improve: Detailer - new detailer wildcard syntax
- [ASC-SIZE], [DSC-SIZE], [SKIP], [STOP]
2024-08-27 00:06:00 +09:00
Dr.Lt.Data a500aff476 fix: RegionalPrompt compatibility with FLUX.1 2024-08-25 16:25:30 +09:00
7 changed files with 118 additions and 79 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
import configparser
import os
version_code = [7, 2, 1]
version_code = [7, 3, 1]
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
dependency_version = 22
+13 -2
View File
@@ -238,11 +238,16 @@ class DetailerForEach:
else:
wmode, wildcard_chooser = None, None
if wmode in ['ASC', 'DSC']:
if wmode in ['ASC', 'DSC', 'ASC-SIZE', 'DSC-SIZE']:
if wmode == 'ASC':
ordered_segs = sorted(segs[1], key=lambda x: (x.bbox[0], x.bbox[1]))
else:
elif wmode == 'DSC':
ordered_segs = sorted(segs[1], key=lambda x: (x.bbox[0], x.bbox[1]), reverse=True)
elif wmode == 'ASC-SIZE':
ordered_segs = sorted(segs[1], key=lambda x: (x.bbox[2]-x.bbox[0]) * (x.bbox[3]-x.bbox[1]))
else: # wmode == 'DSC-SIZE'
ordered_segs = sorted(segs[1], key=lambda x: (x.bbox[2]-x.bbox[0]) * (x.bbox[3]-x.bbox[1]), reverse=True)
else:
ordered_segs = segs[1]
@@ -291,6 +296,12 @@ class DetailerForEach:
# Negative Conditioning is placeholder such as FLUX.1
cropped_negative = negative
if wildcard_item and wildcard_item.strip() == '[SKIP]':
continue
if wildcard_item and wildcard_item.strip() == '[STOP]':
break
enhanced_image, cnet_pils = core.enhance_detail(cropped_image, model, clip, vae, guide_size, guide_size_for_bbox, max_size,
seg.bbox, seg_seed, steps, cfg, sampler_name, scheduler,
cropped_positive, cropped_negative, denoise, cropped_mask, force_inpaint,
+78 -60
View File
@@ -10,7 +10,6 @@ import re
import nodes
import traceback
class ImpactCompare:
@classmethod
def INPUT_TYPES(cls):
@@ -631,85 +630,104 @@ class ImpactControlBridge:
def INPUT_TYPES(cls):
return {"required": {
"value": (any_typ,),
"mode": ("BOOLEAN", {"default": True, "label_on": "Active", "label_off": "Mute/Bypass"}),
"behavior": ("BOOLEAN", {"default": True, "label_on": "Mute", "label_off": "Bypass"}),
"mode": ("BOOLEAN", {"default": True, "label_on": "Active", "label_off": "Stop/Mute/Bypass"}),
"behavior": (["Stop", "Mute", "Bypass"], ),
},
"hidden": {"unique_id": "UNIQUE_ID", "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"}
}
FUNCTION = "doit"
CATEGORY = "ImpactPack/Logic/_for_test"
CATEGORY = "ImpactPack/Logic"
RETURN_TYPES = (any_typ,)
RETURN_NAMES = ("value",)
OUTPUT_NODE = True
DESCRIPTION = ("When behavior is Stop and mode is active, the input value is passed directly to the output.\n"
"When behavior is Mute/Bypass and mode is active, the node connected to the output is changed to active state.\n"
"When behavior is Stop and mode is Stop/Mute/Bypass, the workflow execution of the current node is halted.\n"
"When behavior is Mute/Bypass and mode is Stop/Mute/Bypass, the node connected to the output is changed to Mute/Bypass state.")
@classmethod
def IS_CHANGED(self, value, mode, behavior=True, unique_id=None, prompt=None, extra_pnginfo=None):
# NOTE: extra_pnginfo is not populated for IS_CHANGED.
# so extra_pnginfo is useless in here
try:
workflow = core.current_prompt['extra_data']['extra_pnginfo']['workflow']
except:
print(f"[Impact Pack] core.current_prompt['extra_data']['extra_pnginfo']['workflow']")
return 0
def IS_CHANGED(self, value, mode, behavior="Stop", unique_id=None, prompt=None, extra_pnginfo=None):
if behavior == "Stop":
return value, mode, behavior
else:
# NOTE: extra_pnginfo is not populated for IS_CHANGED.
# so extra_pnginfo is useless in here
try:
workflow = core.current_prompt['extra_data']['extra_pnginfo']['workflow']
except:
print(f"[Impact Pack] core.current_prompt['extra_data']['extra_pnginfo']['workflow']")
return 0
nodes, links = workflow_to_map(workflow)
next_nodes = []
nodes, links = workflow_to_map(workflow)
next_nodes = []
for link in nodes[unique_id]['outputs'][0]['links']:
node_id = str(links[link][2])
impact.utils.collect_non_reroute_nodes(nodes, links, next_nodes, node_id)
for link in nodes[unique_id]['outputs'][0]['links']:
node_id = str(links[link][2])
impact.utils.collect_non_reroute_nodes(nodes, links, next_nodes, node_id)
return next_nodes
def doit(self, value, mode, behavior=True, unique_id=None, prompt=None, extra_pnginfo=None):
def doit(self, value, mode, behavior="Stop", unique_id=None, prompt=None, extra_pnginfo=None):
global error_skip_flag
workflow_nodes, links = workflow_to_map(extra_pnginfo['workflow'])
active_nodes = []
mute_nodes = []
bypass_nodes = []
for link in workflow_nodes[unique_id]['outputs'][0]['links']:
node_id = str(links[link][2])
next_nodes = []
impact.utils.collect_non_reroute_nodes(workflow_nodes, links, next_nodes, node_id)
for next_node_id in next_nodes:
node_mode = workflow_nodes[next_node_id]['mode']
if node_mode == 0:
active_nodes.append(next_node_id)
elif node_mode == 2:
mute_nodes.append(next_node_id)
elif node_mode == 4:
bypass_nodes.append(next_node_id)
if mode:
# active
should_be_active_nodes = mute_nodes + bypass_nodes
if len(should_be_active_nodes) > 0:
PromptServer.instance.send_sync("impact-bridge-continue", {"node_id": unique_id, 'actives': list(should_be_active_nodes)})
nodes.interrupt_processing()
elif behavior:
# mute
should_be_mute_nodes = active_nodes + bypass_nodes
if len(should_be_mute_nodes) > 0:
PromptServer.instance.send_sync("impact-bridge-continue", {"node_id": unique_id, 'mutes': list(should_be_mute_nodes)})
nodes.interrupt_processing()
if core.is_execution_model_version_supported:
from comfy_execution.graph import ExecutionBlocker
else:
# bypass
should_be_bypass_nodes = active_nodes + mute_nodes
if len(should_be_bypass_nodes) > 0:
PromptServer.instance.send_sync("impact-bridge-continue", {"node_id": unique_id, 'bypasses': list(should_be_bypass_nodes)})
nodes.interrupt_processing()
print("[Impact Pack] ImpactControlBridge: ComfyUI is outdated. The 'Stop' behavior cannot function properly.")
return (value, )
if behavior == "Stop":
if mode:
return (value, )
else:
return (ExecutionBlocker(None), )
else:
workflow_nodes, links = workflow_to_map(extra_pnginfo['workflow'])
active_nodes = []
mute_nodes = []
bypass_nodes = []
for link in workflow_nodes[unique_id]['outputs'][0]['links']:
node_id = str(links[link][2])
next_nodes = []
impact.utils.collect_non_reroute_nodes(workflow_nodes, links, next_nodes, node_id)
for next_node_id in next_nodes:
node_mode = workflow_nodes[next_node_id]['mode']
if node_mode == 0:
active_nodes.append(next_node_id)
elif node_mode == 2:
mute_nodes.append(next_node_id)
elif node_mode == 4:
bypass_nodes.append(next_node_id)
if mode:
# active
should_be_active_nodes = mute_nodes + bypass_nodes
if len(should_be_active_nodes) > 0:
PromptServer.instance.send_sync("impact-bridge-continue", {"node_id": unique_id, 'actives': list(should_be_active_nodes)})
nodes.interrupt_processing()
elif behavior == "Mute" or behavior == True:
# mute
should_be_mute_nodes = active_nodes + bypass_nodes
if len(should_be_mute_nodes) > 0:
PromptServer.instance.send_sync("impact-bridge-continue", {"node_id": unique_id, 'mutes': list(should_be_mute_nodes)})
nodes.interrupt_processing()
else:
# bypass
should_be_bypass_nodes = active_nodes + mute_nodes
if len(should_be_bypass_nodes) > 0:
PromptServer.instance.send_sync("impact-bridge-continue", {"node_id": unique_id, 'bypasses': list(should_be_bypass_nodes)})
nodes.interrupt_processing()
return (value, )
class ImpactExecutionOrderController:
+8 -2
View File
@@ -5,7 +5,7 @@ from impact.utils import *
from nodes import MAX_RESOLUTION
import nodes
from impact.impact_sampling import KSamplerWrapper, KSamplerAdvancedWrapper, separated_sample, impact_sample
import comfy
class TiledKSamplerProvider:
@classmethod
@@ -332,6 +332,10 @@ class RegionalSampler:
@staticmethod
def doit(seed, seed_2nd, seed_2nd_mode, steps, base_only_steps, denoise, samples, base_sampler, regional_prompts, overlap_factor, restore_latent,
additional_mode, additional_sampler, additional_sigma_ratio, unique_id=None):
samples = samples.copy()
samples['samples'] = comfy.sample.fix_empty_latent_channels(base_sampler.params[0], samples['samples'])
if restore_latent:
latent_compositor = nodes.NODE_CLASS_MAPPINGS['LatentCompositeMasked']()
else:
@@ -476,6 +480,9 @@ class RegionalSamplerAdvanced:
def doit(add_noise, noise_seed, steps, start_at_step, end_at_step, overlap_factor, restore_latent, return_with_leftover_noise, latent_image, base_sampler, regional_prompts,
additional_mode, additional_sampler, additional_sigma_ratio, unique_id):
new_latent_image = latent_image.copy()
new_latent_image['samples'] = comfy.sample.fix_empty_latent_channels(base_sampler.params[0], new_latent_image['samples'])
if restore_latent:
latent_compositor = nodes.NODE_CLASS_MAPPINGS['LatentCompositeMasked']()
else:
@@ -491,7 +498,6 @@ class RegionalSamplerAdvanced:
end_at_step = min(steps, end_at_step)
total = (end_at_step - start_at_step) * region_len
new_latent_image = latent_image.copy()
base_latent_image = None
region_masks = {}
+2 -1
View File
@@ -1,6 +1,5 @@
from impact.utils import any_typ, ByPassTypeTuple, make_3d_mask
import comfy_extras.nodes_mask
from comfy_execution.graph import ExecutionBlocker
from nodes import MAX_RESOLUTION
import torch
import comfy
@@ -166,6 +165,8 @@ class GeneralInversedSwitch:
def doit(self, select, prompt, unique_id, input, **kwargs):
if core.is_execution_model_version_supported:
from comfy_execution.graph import ExecutionBlocker
else:
print("[Impact Pack] InversedSwitch: ComfyUI is outdated. The 'select_on_execution' mode cannot function properly.")
res = []
+15 -12
View File
@@ -425,7 +425,7 @@ def process_with_loras(wildcard_opt, model, clip, clip_encoder=None, seed=None,
def starts_with_regex(pattern, text):
regex = re.compile(pattern)
return bool(regex.match(text))
return regex.match(text)
def split_to_dict(text):
@@ -507,18 +507,21 @@ def process_wildcard_for_segs(wildcard):
return 'LAB', WildcardChooserDict(items)
elif starts_with_regex(r"\[(ASC|DSC|RND)\]", wildcard):
mode = wildcard[1:4]
items = split_string_with_sep(wildcard[5:])
if mode == 'RND':
random.shuffle(items)
return mode, WildcardChooser(items, True)
else:
return mode, WildcardChooser(items, False)
else:
return None, WildcardChooser([(None, wildcard)], False)
match = starts_with_regex(r"\[(ASC-SIZE|DSC-SIZE|ASC|DSC|RND)\]", wildcard)
if match:
mode = match[1]
items = split_string_with_sep(wildcard[len(match[0]):])
if mode == 'RND':
random.shuffle(items)
return mode, WildcardChooser(items, True)
else:
return mode, WildcardChooser(items, False)
else:
return None, WildcardChooser([(None, wildcard)], False)
def wildcard_load():
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-impact-pack"
description = "This extension offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler."
version = "7.2.1"
version = "7.3.1"
license = { file = "LICENSE.txt" }
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]