Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd69570977 | ||
|
|
1c488eec11 | ||
|
|
c3eed0936f | ||
|
|
44adbbe282 | ||
|
|
940ad41b15 | ||
|
|
e848a9dfb7 | ||
|
|
76653016dc | ||
|
|
9ed160123d | ||
|
|
ffbd4ae116 | ||
|
|
5757c533e6 | ||
|
|
e99e9aaec2 | ||
|
|
690b0a0f61 | ||
|
|
84fbb3fa2e |
@@ -228,11 +228,14 @@ This custom node helps to conveniently enhance images through Detector, Detailer
|
|||||||
|
|
||||||
|
|
||||||
### Batch/List Util
|
### Batch/List Util
|
||||||
* `Image batch To Image List` - Convert Image batch to Image List
|
* `Image Batch to Image List` - Convert Image batch to Image List
|
||||||
- You can use images generated in a multi batch to handle them
|
- You can use images generated in a multi batch to handle them
|
||||||
|
* `Image List to Image Batch` - Convert Image List to Image Batch
|
||||||
* `Make Image List` - Convert multiple images into a single image list
|
* `Make Image List` - Convert multiple images into a single image list
|
||||||
* `Make Image Batch` - Convert multiple images into a single image batch
|
* `Make Image Batch` - Convert multiple images into a single image batch
|
||||||
- The input of images can be scaled up as needed
|
- The input of images can be scaled up as needed
|
||||||
|
* `Masks to Mask List`, `Mask List to Masks`, `Make Mask List`, `Make Mask Batch` - It has the same functionality as the nodes above, but uses mask as input instead of image.
|
||||||
|
* `Flatten Mask Batch` - Flattens a Mask Batch into a single Mask. Normal operation is not guaranteed for non-binary masks.
|
||||||
|
|
||||||
|
|
||||||
### Logics (experimental)
|
### Logics (experimental)
|
||||||
|
|||||||
+11
-3
@@ -161,6 +161,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"ImpactSegsAndMask": SegsBitwiseAndMask,
|
"ImpactSegsAndMask": SegsBitwiseAndMask,
|
||||||
"ImpactSegsAndMaskForEach": SegsBitwiseAndMaskForEach,
|
"ImpactSegsAndMaskForEach": SegsBitwiseAndMaskForEach,
|
||||||
"EmptySegs": EmptySEGS,
|
"EmptySegs": EmptySEGS,
|
||||||
|
"ImpactFlattenMask": FlattenMask,
|
||||||
|
|
||||||
"MediaPipeFaceMeshToSEGS": MediaPipeFaceMeshToSEGS,
|
"MediaPipeFaceMeshToSEGS": MediaPipeFaceMeshToSEGS,
|
||||||
"MaskToSEGS": MaskToSEGS,
|
"MaskToSEGS": MaskToSEGS,
|
||||||
@@ -249,6 +250,8 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"ImpactImageBatchToImageList": ImageBatchToImageList,
|
"ImpactImageBatchToImageList": ImageBatchToImageList,
|
||||||
"ImpactMakeImageList": MakeImageList,
|
"ImpactMakeImageList": MakeImageList,
|
||||||
"ImpactMakeImageBatch": MakeImageBatch,
|
"ImpactMakeImageBatch": MakeImageBatch,
|
||||||
|
"ImpactMakeMaskList": MakeMaskList,
|
||||||
|
"ImpactMakeMaskBatch": MakeMaskBatch,
|
||||||
|
|
||||||
"RegionalSampler": RegionalSampler,
|
"RegionalSampler": RegionalSampler,
|
||||||
"RegionalSamplerAdvanced": RegionalSamplerAdvanced,
|
"RegionalSamplerAdvanced": RegionalSamplerAdvanced,
|
||||||
@@ -332,6 +335,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"BitwiseAndMask": "Pixelwise(MASK & MASK)",
|
"BitwiseAndMask": "Pixelwise(MASK & MASK)",
|
||||||
"SubtractMask": "Pixelwise(MASK - MASK)",
|
"SubtractMask": "Pixelwise(MASK - MASK)",
|
||||||
"AddMask": "Pixelwise(MASK + MASK)",
|
"AddMask": "Pixelwise(MASK + MASK)",
|
||||||
|
"ImpactFlattenMask": "Flatten Mask Batch",
|
||||||
"DetailerForEach": "Detailer (SEGS)",
|
"DetailerForEach": "Detailer (SEGS)",
|
||||||
"DetailerForEachPipe": "Detailer (SEGS/pipe)",
|
"DetailerForEachPipe": "Detailer (SEGS/pipe)",
|
||||||
"DetailerForEachDebug": "DetailerDebug (SEGS)",
|
"DetailerForEachDebug": "DetailerDebug (SEGS)",
|
||||||
@@ -400,12 +404,16 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"ImpactInversedSwitch": "Inversed Switch (Any)",
|
"ImpactInversedSwitch": "Inversed Switch (Any)",
|
||||||
"ImpactExecutionOrderController": "Execution Order Controller",
|
"ImpactExecutionOrderController": "Execution Order Controller",
|
||||||
|
|
||||||
"MasksToMaskList": "Masks to Mask List",
|
"MasksToMaskList": "Mask Batch to Mask List",
|
||||||
"MaskListToMaskBatch": "Mask List to Masks",
|
"MaskListToMaskBatch": "Mask List to Mask Batch",
|
||||||
"ImpactImageBatchToImageList": "Image batch to Image List",
|
"ImpactImageBatchToImageList": "Image Batch to Image List",
|
||||||
"ImageListToImageBatch": "Image List to Image Batch",
|
"ImageListToImageBatch": "Image List to Image Batch",
|
||||||
|
|
||||||
"ImpactMakeImageList": "Make Image List",
|
"ImpactMakeImageList": "Make Image List",
|
||||||
"ImpactMakeImageBatch": "Make Image Batch",
|
"ImpactMakeImageBatch": "Make Image Batch",
|
||||||
|
"ImpactMakeMaskList": "Make Mask List",
|
||||||
|
"ImpactMakeMaskBatch": "Make Mask Batch",
|
||||||
|
|
||||||
"ImpactStringSelector": "String Selector",
|
"ImpactStringSelector": "String Selector",
|
||||||
"StringListToString": "String List to String",
|
"StringListToString": "String List to String",
|
||||||
"WildcardPromptFromString": "Wildcard Prompt from String",
|
"WildcardPromptFromString": "Wildcard Prompt from String",
|
||||||
|
|||||||
+7
-1
@@ -237,7 +237,7 @@ app.registerExtension({
|
|||||||
if(nodeData.name == "ImpactControlBridge") {
|
if(nodeData.name == "ImpactControlBridge") {
|
||||||
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
|
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
|
||||||
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
|
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
|
||||||
if(!link_info || this.inputs[0].type != '*')
|
if(index != 0 || !link_info || this.inputs[0].type != '*')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// assign type
|
// assign type
|
||||||
@@ -393,6 +393,7 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nodeData.name === 'ImpactMakeImageList' || nodeData.name === 'ImpactMakeImageBatch' ||
|
if (nodeData.name === 'ImpactMakeImageList' || nodeData.name === 'ImpactMakeImageBatch' ||
|
||||||
|
nodeData.name === 'ImpactMakeMaskList' || nodeData.name === 'ImpactMakeMaskBatch' ||
|
||||||
nodeData.name === 'CombineRegionalPrompts' ||
|
nodeData.name === 'CombineRegionalPrompts' ||
|
||||||
nodeData.name === 'ImpactCombineConditionings' || nodeData.name === 'ImpactConcatConditionings' ||
|
nodeData.name === 'ImpactCombineConditionings' || nodeData.name === 'ImpactConcatConditionings' ||
|
||||||
nodeData.name === 'ImpactSEGSConcat' ||
|
nodeData.name === 'ImpactSEGSConcat' ||
|
||||||
@@ -405,6 +406,11 @@ app.registerExtension({
|
|||||||
input_name = "image";
|
input_name = "image";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'ImpactMakeMaskList':
|
||||||
|
case 'ImpactMakeMaskBatch':
|
||||||
|
input_name = "mask";
|
||||||
|
break;
|
||||||
|
|
||||||
case 'ImpactSEGSConcat':
|
case 'ImpactSEGSConcat':
|
||||||
input_name = "segs";
|
input_name = "segs";
|
||||||
break;
|
break;
|
||||||
|
|||||||
+11
-7
@@ -6,11 +6,15 @@ let refresh_btn2 = document.querySelector('button[title="Refresh widgets in node
|
|||||||
|
|
||||||
let orig = refresh_btn.onclick;
|
let orig = refresh_btn.onclick;
|
||||||
|
|
||||||
refresh_btn.onclick = function() {
|
if(refresh_btn) {
|
||||||
orig();
|
refresh_btn.onclick = function() {
|
||||||
api.fetchApi('/impact/wildcards/refresh');
|
orig();
|
||||||
};
|
api.fetchApi('/impact/wildcards/refresh');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
refresh_btn2.addEventListener('click', function() {
|
if(refresh_btn2) {
|
||||||
api.fetchApi('/impact/wildcards/refresh');
|
refresh_btn2?.addEventListener('click', function() {
|
||||||
});
|
api.fetchApi('/impact/wildcards/refresh');
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -5,6 +5,13 @@ from impact.core import SEG
|
|||||||
from impact.segs_nodes import SEGSPaste
|
from impact.segs_nodes import SEGSPaste
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from comfy_extras import nodes_differential_diffusion
|
||||||
|
except Exception:
|
||||||
|
print(f"\n#############################################\n[Impact Pack] ComfyUI is an outdated version.\n#############################################\n")
|
||||||
|
raise Exception("[Impact Pack] ComfyUI is an outdated version.")
|
||||||
|
|
||||||
|
|
||||||
class SEGSDetailerForAnimateDiff:
|
class SEGSDetailerForAnimateDiff:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
@@ -53,6 +60,9 @@ class SEGSDetailerForAnimateDiff:
|
|||||||
new_segs = []
|
new_segs = []
|
||||||
cnet_image_list = []
|
cnet_image_list = []
|
||||||
|
|
||||||
|
if noise_mask_feather > 0 and 'denoise_mask_function' not in model.model_options:
|
||||||
|
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
|
||||||
|
|
||||||
for seg in segs[1]:
|
for seg in segs[1]:
|
||||||
cropped_image_frames = None
|
cropped_image_frames = None
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ class PreviewBridge:
|
|||||||
"images": ("IMAGE",),
|
"images": ("IMAGE",),
|
||||||
"image": ("STRING", {"default": ""}),
|
"image": ("STRING", {"default": ""}),
|
||||||
},
|
},
|
||||||
|
"optional": {
|
||||||
|
"block": ("BOOLEAN", {"default": False, "label_on": "if_empty_mask", "label_off": "never", "tooltip": "is_empty_mask: If the mask is empty, the execution is stopped.\nnever: The execution is never stopped."})
|
||||||
|
},
|
||||||
"hidden": {"unique_id": "UNIQUE_ID", "extra_pnginfo": "EXTRA_PNGINFO"},
|
"hidden": {"unique_id": "UNIQUE_ID", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +33,8 @@ class PreviewBridge:
|
|||||||
|
|
||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
|
DESCRIPTION = "This is a feature that allows you to edit and send a Mask over a image.\nIf the block is set to 'is_empty_mask', the execution is stopped when the mask is empty."
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.output_dir = folder_paths.get_temp_directory()
|
self.output_dir = folder_paths.get_temp_directory()
|
||||||
@@ -70,7 +75,7 @@ class PreviewBridge:
|
|||||||
|
|
||||||
return image, mask.unsqueeze(0), ui_item
|
return image, mask.unsqueeze(0), ui_item
|
||||||
|
|
||||||
def doit(self, images, image, unique_id, prompt=None, extra_pnginfo=None):
|
def doit(self, images, image, unique_id, block=False, prompt=None, extra_pnginfo=None):
|
||||||
need_refresh = False
|
need_refresh = False
|
||||||
|
|
||||||
if unique_id not in core.preview_bridge_cache:
|
if unique_id not in core.preview_bridge_cache:
|
||||||
@@ -96,9 +101,20 @@ class PreviewBridge:
|
|||||||
|
|
||||||
image = image2
|
image = image2
|
||||||
|
|
||||||
|
is_empty_mask = torch.all(mask == 0)
|
||||||
|
|
||||||
|
if block and is_empty_mask and core.is_execution_model_version_supported:
|
||||||
|
from comfy_execution.graph import ExecutionBlocker
|
||||||
|
result = ExecutionBlocker(None), ExecutionBlocker(None)
|
||||||
|
elif block and is_empty_mask:
|
||||||
|
print(f"[Impact Pack] PreviewBridge: ComfyUI is outdated - blocking feature is disabled.")
|
||||||
|
result = pixels, mask
|
||||||
|
else:
|
||||||
|
result = pixels, mask
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"ui": {"images": image},
|
"ui": {"images": image},
|
||||||
"result": (pixels, mask, ),
|
"result": result,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -179,7 +195,8 @@ class PreviewBridgeLatent:
|
|||||||
"TAEF1", "TAESDXL", "TAESD15", "TAESD3"],),
|
"TAEF1", "TAESDXL", "TAESD15", "TAESD3"],),
|
||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"vae_opt": ("VAE", )
|
"vae_opt": ("VAE", ),
|
||||||
|
"block": ("BOOLEAN", {"default": False, "label_on": "if_empty_mask", "label_off": "never", "tooltip": "is_empty_mask: If the mask is empty, the execution is stopped.\nnever: The execution is never stopped. Instead, it returns a white mask."})
|
||||||
},
|
},
|
||||||
"hidden": {"unique_id": "UNIQUE_ID", "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
"hidden": {"unique_id": "UNIQUE_ID", "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||||
}
|
}
|
||||||
@@ -192,6 +209,8 @@ class PreviewBridgeLatent:
|
|||||||
|
|
||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
|
DESCRIPTION = "This is a feature that allows you to edit and send a Mask over a latent image.\nIf the block is set to 'is_empty_mask', the execution is stopped when the mask is empty."
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.output_dir = folder_paths.get_temp_directory()
|
self.output_dir = folder_paths.get_temp_directory()
|
||||||
@@ -233,7 +252,7 @@ class PreviewBridgeLatent:
|
|||||||
|
|
||||||
return image, mask, ui_item
|
return image, mask, ui_item
|
||||||
|
|
||||||
def doit(self, latent, image, preview_method, vae_opt=None, unique_id=None, prompt=None, extra_pnginfo=None):
|
def doit(self, latent, image, preview_method, vae_opt=None, block=False, unique_id=None, prompt=None, extra_pnginfo=None):
|
||||||
latent_channels = latent['samples'].shape[1]
|
latent_channels = latent['samples'].shape[1]
|
||||||
preview_method_channels = 16 if 'SD3' in preview_method or 'SC-Prior' in preview_method or 'FLUX.1' in preview_method or 'TAEF1' == preview_method else 4
|
preview_method_channels = 16 if 'SD3' in preview_method or 'SC-Prior' in preview_method or 'FLUX.1' in preview_method or 'TAEF1' == preview_method else 4
|
||||||
|
|
||||||
@@ -262,10 +281,14 @@ class PreviewBridgeLatent:
|
|||||||
del res_latent['noise_mask']
|
del res_latent['noise_mask']
|
||||||
else:
|
else:
|
||||||
res_latent = latent
|
res_latent = latent
|
||||||
|
|
||||||
|
is_empty_mask = True
|
||||||
else:
|
else:
|
||||||
res_latent = latent.copy()
|
res_latent = latent.copy()
|
||||||
res_latent['noise_mask'] = mask
|
res_latent['noise_mask'] = mask
|
||||||
|
|
||||||
|
is_empty_mask = torch.all(mask == 1)
|
||||||
|
|
||||||
res_image = [path_item]
|
res_image = [path_item]
|
||||||
else:
|
else:
|
||||||
decoded_image = decode_latent(latent, preview_method, vae_opt)
|
decoded_image = decode_latent(latent, preview_method, vae_opt)
|
||||||
@@ -287,11 +310,15 @@ class PreviewBridgeLatent:
|
|||||||
'subfolder': 'PreviewBridge',
|
'subfolder': 'PreviewBridge',
|
||||||
'type': 'temp',
|
'type': 'temp',
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
is_empty_mask = torch.all(mask == 1)
|
||||||
else:
|
else:
|
||||||
mask = torch.ones(latent['samples'].shape[2:], dtype=torch.float32, device="cpu").unsqueeze(0)
|
mask = torch.ones(latent['samples'].shape[2:], dtype=torch.float32, device="cpu").unsqueeze(0)
|
||||||
res = nodes.PreviewImage().save_images(decoded_image, filename_prefix="PreviewBridge/PBL-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
res = nodes.PreviewImage().save_images(decoded_image, filename_prefix="PreviewBridge/PBL-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||||
res_image = res['ui']['images']
|
res_image = res['ui']['images']
|
||||||
|
|
||||||
|
is_empty_mask = True
|
||||||
|
|
||||||
path = os.path.join(folder_paths.get_temp_directory(), 'PreviewBridge', res_image[0]['filename'])
|
path = os.path.join(folder_paths.get_temp_directory(), 'PreviewBridge', res_image[0]['filename'])
|
||||||
core.set_previewbridge_image(unique_id, path, res_image[0])
|
core.set_previewbridge_image(unique_id, path, res_image[0])
|
||||||
core.preview_bridge_image_id_map[image] = (path, res_image[0])
|
core.preview_bridge_image_id_map[image] = (path, res_image[0])
|
||||||
@@ -300,7 +327,16 @@ class PreviewBridgeLatent:
|
|||||||
|
|
||||||
res_latent = latent
|
res_latent = latent
|
||||||
|
|
||||||
|
if block and is_empty_mask and core.is_execution_model_version_supported:
|
||||||
|
from comfy_execution.graph import ExecutionBlocker
|
||||||
|
result = ExecutionBlocker(None), ExecutionBlocker(None)
|
||||||
|
elif block and is_empty_mask:
|
||||||
|
print(f"[Impact Pack] PreviewBridgeLatent: ComfyUI is outdated - blocking feature is disabled.")
|
||||||
|
result = res_latent, mask
|
||||||
|
else:
|
||||||
|
result = res_latent, mask
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"ui": {"images": res_image},
|
"ui": {"images": res_image},
|
||||||
"result": (res_latent, mask, ),
|
"result": result,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
|
|
||||||
version_code = [7, 3]
|
version_code = [7, 5, 2]
|
||||||
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||||
|
|
||||||
dependency_version = 22
|
dependency_version = 22
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ def enhance_detail(image, model, clip, vae, guide_size, guide_size_for_bbox, max
|
|||||||
noise_mask = utils.tensor_gaussian_blur_mask(noise_mask, noise_mask_feather)
|
noise_mask = utils.tensor_gaussian_blur_mask(noise_mask, noise_mask_feather)
|
||||||
noise_mask = noise_mask.squeeze(3)
|
noise_mask = noise_mask.squeeze(3)
|
||||||
|
|
||||||
if noise_mask_feather > 0:
|
if noise_mask_feather > 0 and 'denoise_mask_function' not in model.model_options:
|
||||||
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
|
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
|
||||||
|
|
||||||
if wildcard_opt is not None and wildcard_opt != "":
|
if wildcard_opt is not None and wildcard_opt != "":
|
||||||
@@ -383,7 +383,7 @@ def enhance_detail_for_animatediff(image_frames, model, clip, vae, guide_size, g
|
|||||||
noise_mask = utils.tensor_gaussian_blur_mask(noise_mask, noise_mask_feather)
|
noise_mask = utils.tensor_gaussian_blur_mask(noise_mask, noise_mask_feather)
|
||||||
noise_mask = noise_mask.squeeze(3)
|
noise_mask = noise_mask.squeeze(3)
|
||||||
|
|
||||||
if noise_mask_feather > 0:
|
if noise_mask_feather > 0 and 'denoise_mask_function' not in model.model_options:
|
||||||
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
|
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
|
||||||
|
|
||||||
if wildcard_opt is not None and wildcard_opt != "":
|
if wildcard_opt is not None and wildcard_opt != "":
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ class PreviewDetailerHookProvider:
|
|||||||
|
|
||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
|
NOT_IDEMPOTENT = True
|
||||||
|
|
||||||
def doit(self, quality, unique_id):
|
def doit(self, quality, unique_id):
|
||||||
hook = hooks.PreviewDetailerHook(unique_id, quality)
|
hook = hooks.PreviewDetailerHook(unique_id, quality)
|
||||||
return (hook, hook)
|
return hook, hook
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ import impact.wildcards as wildcards
|
|||||||
from . import hooks
|
from . import hooks
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from comfy_extras import nodes_differential_diffusion
|
||||||
|
except Exception:
|
||||||
|
print(f"\n#############################################\n[Impact Pack] ComfyUI is an outdated version.\n#############################################\n")
|
||||||
|
raise Exception("[Impact Pack] ComfyUI is an outdated version.")
|
||||||
|
|
||||||
|
|
||||||
warnings.filterwarnings('ignore', category=UserWarning, message='TypedStorage is deprecated')
|
warnings.filterwarnings('ignore', category=UserWarning, message='TypedStorage is deprecated')
|
||||||
|
|
||||||
model_path = folder_paths.models_dir
|
model_path = folder_paths.models_dir
|
||||||
@@ -62,10 +70,10 @@ class CLIPSegDetectorProvider:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
return {"required": {
|
return {"required": {
|
||||||
"text": ("STRING", {"multiline": False}),
|
"text": ("STRING", {"multiline": False, "tooltip": "Enter the targets to be detected, separated by commas"}),
|
||||||
"blur": ("FLOAT", {"min": 0, "max": 15, "step": 0.1, "default": 7}),
|
"blur": ("FLOAT", {"min": 0, "max": 15, "step": 0.1, "default": 7, "tooltip": "Blurs the detected mask"}),
|
||||||
"threshold": ("FLOAT", {"min": 0, "max": 1, "step": 0.05, "default": 0.4}),
|
"threshold": ("FLOAT", {"min": 0, "max": 1, "step": 0.05, "default": 0.4, "tooltip": "Detects only areas that are certain above the threshold."}),
|
||||||
"dilation_factor": ("INT", {"min": 0, "max": 10, "step": 1, "default": 4}),
|
"dilation_factor": ("INT", {"min": 0, "max": 10, "step": 1, "default": 4, "tooltip": "Dilates the detected mask."}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +82,8 @@ class CLIPSegDetectorProvider:
|
|||||||
|
|
||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
|
DESCRIPTION = "Provides a detection function using CLIPSeg, which generates masks based on text prompts.\nTo use this node, the CLIPSeg custom node must be installed."
|
||||||
|
|
||||||
def doit(self, text, blur, threshold, dilation_factor):
|
def doit(self, text, blur, threshold, dilation_factor):
|
||||||
if "CLIPSeg" in nodes.NODE_CLASS_MAPPINGS:
|
if "CLIPSeg" in nodes.NODE_CLASS_MAPPINGS:
|
||||||
return (core.BBoxDetectorBasedOnCLIPSeg(text, blur, threshold, dilation_factor), )
|
return (core.BBoxDetectorBasedOnCLIPSeg(text, blur, threshold, dilation_factor), )
|
||||||
@@ -87,8 +97,10 @@ class SAMLoader:
|
|||||||
models = [x for x in folder_paths.get_filename_list("sams") if 'hq' not in x]
|
models = [x for x in folder_paths.get_filename_list("sams") if 'hq' not in x]
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"model_name": (models + ['ESAM'], ),
|
"model_name": (models + ['ESAM'], {"tooltip": "The detection accuracy varies depending on the SAM model. ESAM can only be used if ComfyUI-YoloWorld-EfficientSAM is installed."}),
|
||||||
"device_mode": (["AUTO", "Prefer GPU", "CPU"],),
|
"device_mode": (["AUTO", "Prefer GPU", "CPU"], {"tooltip": "AUTO: Only applicable when a GPU is available. It temporarily loads the SAM_MODEL into VRAM only when the detection function is used.\n"
|
||||||
|
"Prefer GPU: Tries to keep the SAM_MODEL on the GPU whenever possible. This can be used when there is sufficient VRAM available.\n"
|
||||||
|
"CPU: Always loads only on the CPU."}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +109,8 @@ class SAMLoader:
|
|||||||
|
|
||||||
CATEGORY = "ImpactPack"
|
CATEGORY = "ImpactPack"
|
||||||
|
|
||||||
|
DESCRIPTION = "Load the SAM (Segment Anything) model. This can be used in places that utilize SAM detection functionality, such as SAMDetector or SimpleDetector.\nThe SAM detection functionality in Impact Pack must use the SAM_MODEL loaded through this node."
|
||||||
|
|
||||||
def load_model(self, model_name, device_mode="auto"):
|
def load_model(self, model_name, device_mode="auto"):
|
||||||
if model_name == 'ESAM':
|
if model_name == 'ESAM':
|
||||||
if 'ESAM_ModelLoader_Zho' not in nodes.NODE_CLASS_MAPPINGS:
|
if 'ESAM_ModelLoader_Zho' not in nodes.NODE_CLASS_MAPPINGS:
|
||||||
@@ -251,6 +265,9 @@ class DetailerForEach:
|
|||||||
else:
|
else:
|
||||||
ordered_segs = segs[1]
|
ordered_segs = segs[1]
|
||||||
|
|
||||||
|
if noise_mask_feather > 0 and 'denoise_mask_function' not in model.model_options:
|
||||||
|
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
|
||||||
|
|
||||||
for i, seg in enumerate(ordered_segs):
|
for i, seg in enumerate(ordered_segs):
|
||||||
cropped_image = crop_ndarray4(image.cpu().numpy(), seg.crop_region) # Never use seg.cropped_image to handle overlapping area
|
cropped_image = crop_ndarray4(image.cpu().numpy(), seg.crop_region) # Never use seg.cropped_image to handle overlapping area
|
||||||
cropped_image = to_tensor(cropped_image)
|
cropped_image = to_tensor(cropped_image)
|
||||||
@@ -1640,6 +1657,8 @@ class BitwiseAndMaskForEach:
|
|||||||
|
|
||||||
CATEGORY = "ImpactPack/Operation"
|
CATEGORY = "ImpactPack/Operation"
|
||||||
|
|
||||||
|
DESCRIPTION = "Retains only the overlapping areas between the masks included in base_segs and the mask regions of mask_segs. SEGS with no overlapping mask areas are filtered out."
|
||||||
|
|
||||||
def doit(self, base_segs, mask_segs):
|
def doit(self, base_segs, mask_segs):
|
||||||
mask = core.segs_to_combined_mask(mask_segs)
|
mask = core.segs_to_combined_mask(mask_segs)
|
||||||
mask = make_3d_mask(mask)
|
mask = make_3d_mask(mask)
|
||||||
@@ -1661,6 +1680,8 @@ class SubtractMaskForEach:
|
|||||||
|
|
||||||
CATEGORY = "ImpactPack/Operation"
|
CATEGORY = "ImpactPack/Operation"
|
||||||
|
|
||||||
|
DESCRIPTION = "Removes only the overlapping areas between the masks included in base_segs and the mask regions of mask_segs. SEGS with no overlapping mask areas are filtered out."
|
||||||
|
|
||||||
def doit(self, base_segs, mask_segs):
|
def doit(self, base_segs, mask_segs):
|
||||||
mask = core.segs_to_combined_mask(mask_segs)
|
mask = core.segs_to_combined_mask(mask_segs)
|
||||||
mask = make_3d_mask(mask)
|
mask = make_3d_mask(mask)
|
||||||
@@ -1686,6 +1707,25 @@ class ToBinaryMask:
|
|||||||
return (mask,)
|
return (mask,)
|
||||||
|
|
||||||
|
|
||||||
|
class FlattenMask:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {
|
||||||
|
"masks": ("MASK",),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("MASK",)
|
||||||
|
FUNCTION = "doit"
|
||||||
|
|
||||||
|
CATEGORY = "ImpactPack/Operation"
|
||||||
|
|
||||||
|
def doit(self, masks):
|
||||||
|
masks = utils.make_3d_mask(masks)
|
||||||
|
masks = utils.flatten_mask(masks)
|
||||||
|
return (masks,)
|
||||||
|
|
||||||
|
|
||||||
class BitwiseAndMask:
|
class BitwiseAndMask:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import impact.core as core
|
|||||||
import re
|
import re
|
||||||
import nodes
|
import nodes
|
||||||
import traceback
|
import traceback
|
||||||
from comfy_execution.graph import ExecutionBlocker
|
|
||||||
|
|
||||||
class ImpactCompare:
|
class ImpactCompare:
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -674,6 +673,11 @@ class ImpactControlBridge:
|
|||||||
def doit(self, value, mode, behavior="Stop", 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
|
global error_skip_flag
|
||||||
|
|
||||||
|
if core.is_execution_model_version_supported:
|
||||||
|
from comfy_execution.graph import ExecutionBlocker
|
||||||
|
else:
|
||||||
|
print("[Impact Pack] ImpactControlBridge: ComfyUI is outdated. The 'Stop' behavior cannot function properly.")
|
||||||
|
|
||||||
if behavior == "Stop":
|
if behavior == "Stop":
|
||||||
if mode:
|
if mode:
|
||||||
return (value, )
|
return (value, )
|
||||||
|
|||||||
@@ -14,6 +14,13 @@ from comfy.cli_args import args
|
|||||||
import math
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from comfy_extras import nodes_differential_diffusion
|
||||||
|
except Exception:
|
||||||
|
print(f"\n#############################################\n[Impact Pack] ComfyUI is an outdated version.\n#############################################\n")
|
||||||
|
raise Exception("[Impact Pack] ComfyUI is an outdated version.")
|
||||||
|
|
||||||
|
|
||||||
class SEGSDetailer:
|
class SEGSDetailer:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
@@ -69,6 +76,9 @@ class SEGSDetailer:
|
|||||||
new_segs = []
|
new_segs = []
|
||||||
cnet_pil_list = []
|
cnet_pil_list = []
|
||||||
|
|
||||||
|
if noise_mask_feather > 0 and 'denoise_mask_function' not in model.model_options:
|
||||||
|
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
|
||||||
|
|
||||||
for i in range(batch_size):
|
for i in range(batch_size):
|
||||||
seed += 1
|
seed += 1
|
||||||
for seg in segs[1]:
|
for seg in segs[1]:
|
||||||
@@ -1403,8 +1413,6 @@ class SEGSPicker:
|
|||||||
|
|
||||||
RETURN_TYPES = ("SEGS", )
|
RETURN_TYPES = ("SEGS", )
|
||||||
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
|
|
||||||
FUNCTION = "doit"
|
FUNCTION = "doit"
|
||||||
|
|
||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ def img2img_segs(image, model, clip, vae, seed, steps, cfg, sampler_name, schedu
|
|||||||
noise_mask = tensor_gaussian_blur_mask(noise_mask, noise_mask_feather)
|
noise_mask = tensor_gaussian_blur_mask(noise_mask, noise_mask_feather)
|
||||||
noise_mask = noise_mask.squeeze(3)
|
noise_mask = noise_mask.squeeze(3)
|
||||||
|
|
||||||
if noise_mask_feather > 0:
|
if noise_mask_feather > 0 and 'denoise_mask_function' not in model.model_options:
|
||||||
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
|
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
|
||||||
|
|
||||||
if control_net_wrapper is not None:
|
if control_net_wrapper is not None:
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from impact.utils import any_typ, ByPassTypeTuple, make_3d_mask
|
from impact.utils import any_typ, ByPassTypeTuple, make_3d_mask
|
||||||
import comfy_extras.nodes_mask
|
import comfy_extras.nodes_mask
|
||||||
from comfy_execution.graph import ExecutionBlocker
|
|
||||||
from nodes import MAX_RESOLUTION
|
from nodes import MAX_RESOLUTION
|
||||||
import torch
|
import torch
|
||||||
import comfy
|
import comfy
|
||||||
@@ -166,6 +165,8 @@ class GeneralInversedSwitch:
|
|||||||
def doit(self, select, prompt, unique_id, input, **kwargs):
|
def doit(self, select, prompt, unique_id, input, **kwargs):
|
||||||
if core.is_execution_model_version_supported:
|
if core.is_execution_model_version_supported:
|
||||||
from comfy_execution.graph import ExecutionBlocker
|
from comfy_execution.graph import ExecutionBlocker
|
||||||
|
else:
|
||||||
|
print("[Impact Pack] InversedSwitch: ComfyUI is outdated. The 'select_on_execution' mode cannot function properly.")
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
|
|
||||||
@@ -391,6 +392,26 @@ class ImageBatchToImageList:
|
|||||||
return (images, )
|
return (images, )
|
||||||
|
|
||||||
|
|
||||||
|
class MakeMaskList:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {"mask1": ("MASK",), }}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("MASK",)
|
||||||
|
OUTPUT_IS_LIST = (True,)
|
||||||
|
FUNCTION = "doit"
|
||||||
|
|
||||||
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
|
def doit(self, **kwargs):
|
||||||
|
masks = []
|
||||||
|
|
||||||
|
for k, v in kwargs.items():
|
||||||
|
masks.append(v)
|
||||||
|
|
||||||
|
return (masks, )
|
||||||
|
|
||||||
|
|
||||||
class MakeImageList:
|
class MakeImageList:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
@@ -436,6 +457,31 @@ class MakeImageBatch:
|
|||||||
return (image1,)
|
return (image1,)
|
||||||
|
|
||||||
|
|
||||||
|
class MakeMaskBatch:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {"mask1": ("MASK",), }}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("MASK",)
|
||||||
|
FUNCTION = "doit"
|
||||||
|
|
||||||
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
|
def doit(self, **kwargs):
|
||||||
|
mask1 = kwargs['mask1']
|
||||||
|
del kwargs['mask1']
|
||||||
|
masks = [utils.make_3d_mask(value) for value in kwargs.values()]
|
||||||
|
|
||||||
|
if len(masks) == 0:
|
||||||
|
return (mask1,)
|
||||||
|
else:
|
||||||
|
for mask2 in masks:
|
||||||
|
if mask1.shape[1:] != mask2.shape[1:]:
|
||||||
|
mask2 = comfy.utils.common_upscale(mask2.movedim(-1, 1), mask1.shape[2], mask1.shape[1], "lanczos", "center").movedim(1, -1)
|
||||||
|
mask1 = torch.cat((mask1, mask2), dim=0)
|
||||||
|
return (mask1,)
|
||||||
|
|
||||||
|
|
||||||
class ReencodeLatent:
|
class ReencodeLatent:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import numpy as np
|
|||||||
import folder_paths
|
import folder_paths
|
||||||
import nodes
|
import nodes
|
||||||
from . import config
|
from . import config
|
||||||
from PIL import Image, ImageFilter
|
from PIL import Image
|
||||||
from scipy.ndimage import zoom
|
|
||||||
import comfy
|
import comfy
|
||||||
|
|
||||||
|
|
||||||
@@ -579,6 +578,14 @@ def apply_mask_alpha_to_pil(decoded_pil, mask):
|
|||||||
return decoded_rgba
|
return decoded_rgba
|
||||||
|
|
||||||
|
|
||||||
|
def flatten_mask(all_masks):
|
||||||
|
merged_mask = (all_masks[0] * 255).to(torch.uint8)
|
||||||
|
for mask in all_masks[1:]:
|
||||||
|
merged_mask |= (mask * 255).to(torch.uint8)
|
||||||
|
|
||||||
|
return merged_mask
|
||||||
|
|
||||||
|
|
||||||
def try_install_custom_node(custom_node_url, msg):
|
def try_install_custom_node(custom_node_url, msg):
|
||||||
try:
|
try:
|
||||||
import cm_global
|
import cm_global
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-impact-pack"
|
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."
|
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.3"
|
version = "7.5.2"
|
||||||
license = { file = "LICENSE.txt" }
|
license = { file = "LICENSE.txt" }
|
||||||
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]
|
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]
|
||||||
|
|
||||||
|
|||||||
@@ -6,3 +6,4 @@ opencv-python-headless
|
|||||||
GitPython
|
GitPython
|
||||||
scipy>=1.11.4
|
scipy>=1.11.4
|
||||||
numpy<2
|
numpy<2
|
||||||
|
dill
|
||||||
Reference in New Issue
Block a user