Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e35bc23fd1 | ||
|
|
869ac6fd1f | ||
|
|
cb168d64ab | ||
|
|
a89e9e01a6 | ||
|
|
e70b4df9b5 | ||
|
|
af1ef7e441 |
@@ -1,35 +0,0 @@
|
||||
import { ComfyApp, app } from "../../scripts/app.js";
|
||||
|
||||
let conflict_check = undefined;
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.impact.comboBoolMigration",
|
||||
|
||||
nodeCreated(node, app) {
|
||||
for(let i in node.widgets) {
|
||||
let widget = node.widgets[i];
|
||||
|
||||
if(conflict_check == undefined) {
|
||||
conflict_check = !!app.extensions.find((ext) => ext.name === "Comfy.comboBoolMigration");
|
||||
}
|
||||
|
||||
if(conflict_check)
|
||||
return;
|
||||
|
||||
if(widget.type == "toggle") {
|
||||
let value = widget.value;
|
||||
|
||||
var v = Object.getOwnPropertyDescriptor(widget, 'value');
|
||||
if(!v) {
|
||||
Object.defineProperty(widget, "value", {
|
||||
set: (value) => {
|
||||
delete widget.value;
|
||||
widget.value = value == true || value == widget.options.on;
|
||||
},
|
||||
get: () => { return value; }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
+11
-5
@@ -93,7 +93,7 @@ const input_dirty = {};
|
||||
const output_tracking = {};
|
||||
|
||||
function progressExecuteHandler(event) {
|
||||
if(event.detail.output.aux){
|
||||
if(event.detail?.output?.aux){
|
||||
const id = event.detail.node;
|
||||
if(input_tracking.hasOwnProperty(id)) {
|
||||
if(input_tracking.hasOwnProperty(id) && input_tracking[id][0] != event.detail.output.aux[0]) {
|
||||
@@ -273,7 +273,7 @@ app.registerExtension({
|
||||
}
|
||||
else {
|
||||
const node = app.graph.getNodeById(link_info.origin_id);
|
||||
slot_type = node.outputs[link_info.origin_slot].type;
|
||||
slot_type = node.outputs[link_info.origin_slot]?.type;
|
||||
}
|
||||
|
||||
this.inputs[0].type = slot_type;
|
||||
@@ -759,14 +759,20 @@ app.registerExtension({
|
||||
// mode combo
|
||||
Object.defineProperty(mode_widget, "value", {
|
||||
set: (value) => {
|
||||
node._mode_value = value == true || value == "Populate";
|
||||
populated_text_widget.inputEl.disabled = value == true || value == "Populate";
|
||||
if(value == true)
|
||||
node._mode_value = "populate";
|
||||
else if(value == false)
|
||||
node._mode_value = "fixed";
|
||||
else
|
||||
node._mode_value = value; // combo value
|
||||
|
||||
populated_text_widget.inputEl.disabled = node._mode_value != 'populate';
|
||||
},
|
||||
get: () => {
|
||||
if(node._mode_value != undefined)
|
||||
return node._mode_value;
|
||||
else
|
||||
return true;
|
||||
return 'populate';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,8 @@ class SEGSDetailerForAnimateDiff:
|
||||
|
||||
CATEGORY = "ImpactPack/Detailer"
|
||||
|
||||
DESCRIPTION = "This node enhances details by inpainting each region within the detected area bundle (SEGS) after enlarging them based on the guide size.\nThis node is applied specifically to SEGS rather than the entire image. To apply it to the entire image, use the 'SEGS Paste' node.\nAs a specialized detailer node for improving video details, such as in AnimateDiff, this node can handle cases where the masks contained in SEGS serve as batch masks spanning multiple frames."
|
||||
|
||||
@staticmethod
|
||||
def do_detail(image_frames, segs, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler,
|
||||
denoise, basic_pipe, refiner_ratio=None, refiner_basic_pipe_opt=None, noise_mask_feather=0, scheduler_func_opt=None):
|
||||
@@ -166,6 +168,8 @@ class DetailerForEachPipeForAnimateDiff:
|
||||
|
||||
CATEGORY = "ImpactPack/Detailer"
|
||||
|
||||
DESCRIPTION = "This node enhances details by inpainting each region within the detected area bundle (SEGS) after enlarging them based on the guide size.\nThis node is a specialized detailer node for enhancing video details, such as in AnimateDiff. It can handle cases where the masks contained in SEGS serve as batch masks spanning multiple frames."
|
||||
|
||||
@staticmethod
|
||||
def doit(image_frames, segs, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler,
|
||||
denoise, feather, basic_pipe, refiner_ratio=None, detailer_hook=None, refiner_basic_pipe_opt=None,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import configparser
|
||||
import os
|
||||
|
||||
version_code = [8, 4, 1]
|
||||
version_code = [8, 7]
|
||||
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||
|
||||
dependency_version = 24
|
||||
|
||||
@@ -228,6 +228,8 @@ class DetailerForEach:
|
||||
|
||||
CATEGORY = "ImpactPack/Detailer"
|
||||
|
||||
DESCRIPTION = "It enhances details by inpainting each region within the detected area bundle (SEGS) after enlarging them based on the guide size."
|
||||
|
||||
@staticmethod
|
||||
def get_core_module():
|
||||
return core
|
||||
@@ -442,6 +444,8 @@ class DetailerForEachPipe:
|
||||
|
||||
CATEGORY = "ImpactPack/Detailer"
|
||||
|
||||
DESCRIPTION = DetailerForEach.DESCRIPTION
|
||||
|
||||
def doit(self, image, segs, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler,
|
||||
denoise, feather, noise_mask, force_inpaint, basic_pipe, wildcard,
|
||||
refiner_ratio=None, detailer_hook=None, refiner_basic_pipe_opt=None,
|
||||
@@ -533,6 +537,8 @@ class FaceDetailer:
|
||||
|
||||
CATEGORY = "ImpactPack/Simple"
|
||||
|
||||
DESCRIPTION = "This node enhances details by automatically detecting specific objects in the input image using detection models (bbox, segm, sam) and regenerating the image by enlarging the detected area based on the guide size.\nAlthough this node is specialized to simplify the commonly used facial detail enhancement workflow, it can also be used for various automatic inpainting purposes depending on the detection model."
|
||||
|
||||
@staticmethod
|
||||
def enhance_face(image, model, clip, vae, guide_size, guide_size_for_bbox, max_size, seed, steps, cfg, sampler_name, scheduler,
|
||||
positive, negative, denoise, feather, noise_mask, force_inpaint,
|
||||
@@ -1407,6 +1413,8 @@ class FaceDetailerPipe:
|
||||
|
||||
CATEGORY = "ImpactPack/Simple"
|
||||
|
||||
DESCRIPTION = FaceDetailer.DESCRIPTION
|
||||
|
||||
def doit(self, image, detailer_pipe, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler,
|
||||
denoise, feather, noise_mask, force_inpaint, bbox_threshold, bbox_dilation, bbox_crop_factor,
|
||||
sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion,
|
||||
@@ -1502,6 +1510,8 @@ class MaskDetailerPipe:
|
||||
|
||||
CATEGORY = "ImpactPack/Detailer"
|
||||
|
||||
DESCRIPTION = ""
|
||||
|
||||
def doit(self, 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,
|
||||
@@ -1607,6 +1617,8 @@ class DetailerForEachTestPipe(DetailerForEachPipe):
|
||||
|
||||
CATEGORY = "ImpactPack/Detailer"
|
||||
|
||||
DESCRIPTION = DetailerForEach.DESCRIPTION
|
||||
|
||||
def doit(self, image, segs, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler,
|
||||
denoise, feather, noise_mask, force_inpaint, basic_pipe, wildcard, cycle=1,
|
||||
refiner_ratio=None, detailer_hook=None, refiner_basic_pipe_opt=None, inpaint_model=False, noise_mask_feather=0,
|
||||
@@ -2343,7 +2355,11 @@ class ImpactWildcardProcessor:
|
||||
return {"required": {
|
||||
"wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False, "tooltip": "Enter a prompt using wildcard syntax."}),
|
||||
"populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False, "tooltip": "The actual value passed during the execution of 'ImpactWildcardProcessor' is what is shown here. The behavior varies slightly depending on the mode. Wildcard syntax can also be used in 'populated_text'."}),
|
||||
"mode": ("BOOLEAN", {"default": True, "label_on": "Populate", "label_off": "Fixed", "tooltip": "Populate: Before running the workflow, it overwrites the existing value of 'populated_text' with the prompt processed from 'wildcard_text'. In this mode, 'populated_text' cannot be edited.\nFixed: Ignores wildcard_text and keeps 'populated_text' as is. You can edit 'populated_text' in this mode."}),
|
||||
"mode": (["populate", "fixed", "reproduce"], {"default": "populate", "tooltip":
|
||||
"populate: Before running the workflow, it overwrites the existing value of 'populated_text' with the prompt processed from 'wildcard_text'. In this mode, 'populated_text' cannot be edited.\n"
|
||||
"fixed: Ignores wildcard_text and keeps 'populated_text' as is. You can edit 'populated_text' in this mode.\n"
|
||||
"reproduce: This mode operates as 'fixed' mode only once for reproduction, and then it switches to 'populate' mode."
|
||||
}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "tooltip": "Determines the random seed to be used for wildcard processing."}),
|
||||
"Select to add Wildcard": (["Select the Wildcard to add to the text"],),
|
||||
},
|
||||
@@ -2352,9 +2368,10 @@ class ImpactWildcardProcessor:
|
||||
CATEGORY = "ImpactPack/Prompt"
|
||||
|
||||
DESCRIPTION = ("The 'ImpactWildcardProcessor' processes text prompts written in wildcard syntax and outputs the processed text prompt.\n\n"
|
||||
"TIP: Before the workflow is executed, the processing result of 'wildcard_text' is displayed in 'populated_text', and the populated text is saved along with the workflow. If you want to use a seed converted as input, write the prompt directly in 'populated_text' instead of 'wildcard_text', and set the mode to 'Fixed'.")
|
||||
"TIP: Before the workflow is executed, the processing result of 'wildcard_text' is displayed in 'populated_text', and the populated text is saved along with the workflow. If you want to use a seed converted as input, write the prompt directly in 'populated_text' instead of 'wildcard_text', and set the mode to 'fixed'.")
|
||||
|
||||
RETURN_TYPES = ("STRING", )
|
||||
RETURN_NAMES = ("processed text",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
@staticmethod
|
||||
@@ -2374,8 +2391,10 @@ class ImpactWildcardEncode:
|
||||
"clip": ("CLIP",),
|
||||
"wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False, "tooltip": "Enter a prompt using wildcard syntax."}),
|
||||
"populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False, "tooltip": "The actual value passed during the execution of 'ImpactWildcardEncode' is what is shown here. The behavior varies slightly depending on the mode. Wildcard syntax can also be used in 'populated_text'."}),
|
||||
"mode": ("BOOLEAN", {"default": True, "label_on": "Populate", "label_off": "Fixed", "tooltip": "Populate: Before running the workflow, it overwrites the existing value of 'populated_text' with the prompt processed from 'wildcard_text'. In this mode, 'populated_text' cannot be edited.\n"
|
||||
"Fixed: Ignores wildcard_text and keeps 'populated_text' as is. You can edit 'populated_text' in this mode."}),
|
||||
"mode": (["populate", "fixed", "reproduce"], {"tooltip":
|
||||
"populate: Before running the workflow, it overwrites the existing value of 'populated_text' with the prompt processed from 'wildcard_text'. In this mode, 'populated_text' cannot be edited.\n"
|
||||
"fixed: Ignores wildcard_text and keeps 'populated_text' as is. You can edit 'populated_text' in this mode\n."
|
||||
"reproduce: This mode operates as 'fixed' mode only once for reproduction, and then it switches to 'populate' mode."}),
|
||||
"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"], ),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "tooltip": "Determines the random seed to be used for wildcard processing."}),
|
||||
@@ -2385,7 +2404,7 @@ class ImpactWildcardEncode:
|
||||
CATEGORY = "ImpactPack/Prompt"
|
||||
|
||||
DESCRIPTION = ("The 'ImpactWildcardEncode' node processes text prompts written in wildcard syntax and outputs them as conditioning. It also supports LoRA syntax, with the applied LoRA reflected in the model's output.\n\n"
|
||||
"TIP1: Before the workflow is executed, the processing result of 'wildcard_text' is displayed in 'populated_text', and the populated text is saved along with the workflow. If you want to use a seed converted as input, write the prompt directly in 'populated_text' instead of 'wildcard_text', and set the mode to 'Fixed'.\n"
|
||||
"TIP1: Before the workflow is executed, the processing result of 'wildcard_text' is displayed in 'populated_text', and the populated text is saved along with the workflow. If you want to use a seed converted as input, write the prompt directly in 'populated_text' instead of 'wildcard_text', and set the mode to 'fixed'.\n"
|
||||
"TIP2: If the 'Inspire Pack' is installed, LBW(LoRA Block Weight) syntax can also be applied.")
|
||||
|
||||
RETURN_TYPES = ("MODEL", "CLIP", "CONDITIONING", "STRING")
|
||||
|
||||
@@ -78,9 +78,13 @@ async def sam_prepare(request):
|
||||
if data['sam_model_name'] == 'auto':
|
||||
model_name = impact.config.get_config()['sam_editor_model']
|
||||
|
||||
model_name = os.path.join(impact_pack.model_path, "sams", model_name)
|
||||
model_path = folder_paths.get_full_path("sams", model_name)
|
||||
|
||||
logging.info(f"[Impact Pack] Loading SAM model '{impact_pack.model_path}'")
|
||||
if model_path is None:
|
||||
logging.error(f"[Impact Pack] The '{model_name}' model file cannot be found in any sams model path.")
|
||||
return web.Response(status=400)
|
||||
|
||||
logging.info(f"[Impact Pack] Loading SAM model '{model_path}'")
|
||||
|
||||
filename, image_dir = folder_paths.annotated_filepath(data["filename"])
|
||||
|
||||
@@ -93,7 +97,7 @@ async def sam_prepare(request):
|
||||
if image_dir is None:
|
||||
return web.Response(status=400)
|
||||
|
||||
thread = threading.Thread(target=async_prepare_sam, args=(image_dir, model_name, filename,))
|
||||
thread = threading.Thread(target=async_prepare_sam, args=(image_dir, model_path, filename,))
|
||||
thread.start()
|
||||
|
||||
logging.info("[Impact Pack] SAM model loaded. ")
|
||||
@@ -478,7 +482,17 @@ def onprompt_populate_wildcards(json_data):
|
||||
for k, v in prompt.items():
|
||||
if 'class_type' in v and (v['class_type'] == 'ImpactWildcardEncode' or v['class_type'] == 'ImpactWildcardProcessor'):
|
||||
inputs = v['inputs']
|
||||
if inputs['mode'] and isinstance(inputs['populated_text'], str):
|
||||
|
||||
# legacy adapter
|
||||
if isinstance(inputs['mode'], bool):
|
||||
if inputs['mode']:
|
||||
new_mode = 'populate'
|
||||
else:
|
||||
new_mode = 'fixed'
|
||||
|
||||
inputs['mode'] = new_mode
|
||||
|
||||
if inputs['mode'] == 'populate' and isinstance(inputs['populated_text'], str):
|
||||
if isinstance(inputs['seed'], list):
|
||||
try:
|
||||
input_node = prompt[inputs['seed'][0]]
|
||||
@@ -499,17 +513,22 @@ def onprompt_populate_wildcards(json_data):
|
||||
input_seed = int(inputs['seed'])
|
||||
|
||||
inputs['populated_text'] = wildcards.process(inputs['wildcard_text'], input_seed)
|
||||
inputs['mode'] = False
|
||||
inputs['mode'] = 'reproduce'
|
||||
|
||||
PromptServer.instance.send_sync("impact-node-feedback", {"node_id": k, "widget_name": "populated_text", "type": "STRING", "value": inputs['populated_text']})
|
||||
updated_widget_values[k] = inputs['populated_text']
|
||||
|
||||
if inputs['mode'] == 'reproduce':
|
||||
PromptServer.instance.send_sync("impact-node-feedback", {"node_id": k, "widget_name": "mode", "type": "STRING", "value": 'populate'})
|
||||
|
||||
|
||||
|
||||
if 'extra_data' in json_data and 'extra_pnginfo' in json_data['extra_data']:
|
||||
for node in json_data['extra_data']['extra_pnginfo']['workflow']['nodes']:
|
||||
key = str(node['id'])
|
||||
if key in updated_widget_values:
|
||||
node['widgets_values'][1] = updated_widget_values[key]
|
||||
node['widgets_values'][2] = False
|
||||
node['widgets_values'][2] = 'reproduce'
|
||||
|
||||
|
||||
def onprompt_for_remote(json_data):
|
||||
|
||||
@@ -60,6 +60,8 @@ class SEGSDetailer:
|
||||
|
||||
CATEGORY = "ImpactPack/Detailer"
|
||||
|
||||
DESCRIPTION = "This node enhances details by inpainting each region within the detected area bundle (SEGS) after enlarging them based on the guide size.\nThis node is applied specifically to SEGS rather than the entire image. To apply it to the entire image, use the 'SEGS Paste' node."
|
||||
|
||||
@staticmethod
|
||||
def do_detail(image, segs, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler,
|
||||
denoise, noise_mask, force_inpaint, basic_pipe, refiner_ratio=None, batch_size=1, cycle=1,
|
||||
@@ -174,6 +176,8 @@ class SEGSPaste:
|
||||
|
||||
CATEGORY = "ImpactPack/Detailer"
|
||||
|
||||
DESCRIPTION = "This node provides a function to paste the enhanced SEGS, improved through the SEGS detailer, back onto the original image."
|
||||
|
||||
@staticmethod
|
||||
def doit(image, segs, feather, alpha=255, ref_image_opt=None):
|
||||
|
||||
@@ -1486,6 +1490,8 @@ class SEGSPicker:
|
||||
|
||||
CATEGORY = "ImpactPack/Util"
|
||||
|
||||
DESCRIPTION = "This node provides a function to select only the chosen SEGS from the input SEGS."
|
||||
|
||||
@staticmethod
|
||||
def doit(picks, segs, fallback_image_opt=None, unique_id=None):
|
||||
if fallback_image_opt is not None:
|
||||
@@ -1542,6 +1548,8 @@ class DefaultImageForSEGS:
|
||||
|
||||
CATEGORY = "ImpactPack/Util"
|
||||
|
||||
DESCRIPTION = "If the SEGS have not passed through the detailer, they contain only detection area information without an image. This node sets a default image for the SEGS."
|
||||
|
||||
@staticmethod
|
||||
def doit(segs, image, override):
|
||||
results = []
|
||||
|
||||
@@ -237,7 +237,23 @@ def process(text, seed=None):
|
||||
keyword = match.lower()
|
||||
keyword = wildcard_normalize(keyword)
|
||||
if keyword in local_wildcard_dict:
|
||||
replacement = random_gen.choice(local_wildcard_dict[keyword])
|
||||
# look for adjusted probability
|
||||
adjusted_probabilities = []
|
||||
total_prob = 0
|
||||
options=local_wildcard_dict[keyword]
|
||||
for option in options:
|
||||
parts = option.split('::', 1)
|
||||
if len(parts) == 2 and is_numeric_string(parts[0].strip()):
|
||||
config_value = float(parts[0].strip())
|
||||
else:
|
||||
config_value = 1 # Default value if no configuration is provided
|
||||
|
||||
adjusted_probabilities.append(config_value)
|
||||
total_prob += config_value
|
||||
|
||||
normalized_probabilities = [prob / total_prob for prob in adjusted_probabilities]
|
||||
selected_item = random_gen.choice(options, p=normalized_probabilities, replace=False)
|
||||
replacement = re.sub(r'^\s*[0-9.]+::', '', selected_item, 1)
|
||||
replacements_found = True
|
||||
string = string.replace(f"__{match}__", replacement, 1)
|
||||
elif '*' in keyword:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-impact-pack"
|
||||
description = "This node pack offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler."
|
||||
version = "8.4.1"
|
||||
version = "8.7"
|
||||
license = { file = "LICENSE.txt" }
|
||||
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user