feat: PreviewDetailerHook
This commit is contained in:
@@ -136,11 +136,12 @@ This custom node helps to conveniently enhance images through Detector, Detailer
|
||||
* DETAILER_HOOK
|
||||
* NoiseInjectionDetailerHookProvider - The `detailer_hook` is a hook in the `Detailer` that injects noise during the processing of each SEGS.
|
||||
* UnsamplerDetailerHookProvider - Apply Unsampler during each cycle. To use this node, ComfyUI_Noise must be installed.
|
||||
* There is a bug in applying the noise mask to the current Unsampler, so this [ComfyUI_Noise/PR-13](https://github.com/BlenderNeko/ComfyUI_Noise/pull/13) must be applied for it to be usable.
|
||||
* DenoiseSchedulerDetailerHookProvider - During the progress of the cycle, the detailer's denoise is altered up to the `target_denoise`.
|
||||
* CoreMLDetailerHookProvider - CoreML supports only 512x512, 512x768, 768x512, 768x768 size sampling. CoreMLDetailerHookProvider precisely fixes the upscale of the crop_region to this size. When using this hook, it will always be selected size, regardless of the guide_size. However, if the guide_size is too small, skipping will occur.
|
||||
* DetailerHookCombine - This is used to connect two DETAILER_HOOKs. Similar to PixelKSampleHookCombine.
|
||||
* SEGSOrderedFilterDetailerHook, SEGSRangeFilterDetailerHook, SEGSLabelFilterDetailerHook - There are a wrapper node that provides SEGSFilter nodes to be applied in FaceDetailer or Detector by creating DETAILER_HOOK.
|
||||
* PreviewDetailerHOok - Connecting this hook node helps provide assistance for viewing previews whenever SEGS Detailing tasks are completed. When working with a large number of SEGS, such as Make Tile SEGS, it allows for monitoring the situation as improvements progress incrementally.
|
||||
* Since this is the hook applied when pasting onto the original image, it has no effect on nodes like `SEGSDetailer`.
|
||||
|
||||
* Iterative Upscale (Latent/on Pixel Space) - The upscaler takes the input upscaler and splits the scale_factor into steps, then iteratively performs upscaling.
|
||||
This takes latent as input and outputs latent as the result.
|
||||
|
||||
@@ -180,6 +180,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"NoiseInjectionHookProvider": NoiseInjectionHookProvider,
|
||||
"UnsamplerHookProvider": UnsamplerHookProvider,
|
||||
"CoreMLDetailerHookProvider": CoreMLDetailerHookProvider,
|
||||
"PreviewDetailerHookProvider": PreviewDetailerHookProvider,
|
||||
|
||||
"DetailerHookCombine": DetailerHookCombine,
|
||||
"NoiseInjectionDetailerHookProvider": NoiseInjectionDetailerHookProvider,
|
||||
|
||||
@@ -9,18 +9,18 @@ app.registerExtension({
|
||||
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 == undefined) {
|
||||
conflict_check = !!app.extensions.find((ext) => ext.name === "Comfy.comboBoolMigration");
|
||||
}
|
||||
|
||||
if(conflict_check)
|
||||
return;
|
||||
if(conflict_check)
|
||||
return;
|
||||
|
||||
if(widget.type == "toggle") {
|
||||
let value = widget.value;
|
||||
let value = widget.value;
|
||||
|
||||
var v = Object.getOwnPropertyDescriptor(widget, 'value');
|
||||
if(!v) {
|
||||
var v = Object.getOwnPropertyDescriptor(widget, 'value');
|
||||
if(!v) {
|
||||
Object.defineProperty(widget, "value", {
|
||||
set: (value) => {
|
||||
delete widget.value;
|
||||
|
||||
@@ -80,3 +80,16 @@ function addQueue(event) {
|
||||
}
|
||||
|
||||
api.addEventListener("impact-add-queue", addQueue);
|
||||
|
||||
|
||||
function refreshPreview(event) {
|
||||
let node_id = event.detail.node_id;
|
||||
let item = event.detail.item;
|
||||
let img = new Image();
|
||||
img.src = `/view?filename=${item.filename}&subfolder=${item.subfolder}&type=${item.type}&no-cache=${Date.now()}`;
|
||||
let node = app.graph._nodes_by_id[node_id];
|
||||
if(node)
|
||||
node.imgs = [img];
|
||||
}
|
||||
|
||||
api.addEventListener("impact-preview", refreshPreview);
|
||||
|
||||
+24
-24
@@ -9,31 +9,31 @@ function load_image(str) {
|
||||
|
||||
function getFileItem(baseType, path) {
|
||||
try {
|
||||
let pathType = baseType;
|
||||
let pathType = baseType;
|
||||
|
||||
if (path.endsWith("[output]")) {
|
||||
pathType = "output";
|
||||
path = path.slice(0, -9);
|
||||
} else if (path.endsWith("[input]")) {
|
||||
pathType = "input";
|
||||
path = path.slice(0, -8);
|
||||
} else if (path.endsWith("[temp]")) {
|
||||
pathType = "temp";
|
||||
path = path.slice(0, -7);
|
||||
}
|
||||
if (path.endsWith("[output]")) {
|
||||
pathType = "output";
|
||||
path = path.slice(0, -9);
|
||||
} else if (path.endsWith("[input]")) {
|
||||
pathType = "input";
|
||||
path = path.slice(0, -8);
|
||||
} else if (path.endsWith("[temp]")) {
|
||||
pathType = "temp";
|
||||
path = path.slice(0, -7);
|
||||
}
|
||||
|
||||
const subfolder = path.substring(0, path.lastIndexOf('/'));
|
||||
const filename = path.substring(path.lastIndexOf('/') + 1);
|
||||
const subfolder = path.substring(0, path.lastIndexOf('/'));
|
||||
const filename = path.substring(path.lastIndexOf('/') + 1);
|
||||
|
||||
return {
|
||||
filename: filename,
|
||||
subfolder: subfolder,
|
||||
type: pathType
|
||||
};
|
||||
}
|
||||
catch(exception) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
filename: filename,
|
||||
subfolder: subfolder,
|
||||
type: pathType
|
||||
};
|
||||
}
|
||||
catch(exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadImageFromUrl(image, node_id, v, need_to_load) {
|
||||
@@ -225,5 +225,5 @@ app.registerExtension({
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -139,6 +139,10 @@ class DetailerForEachPipeForAnimateDiff:
|
||||
denoise, basic_pipe, refiner_ratio, refiner_basic_pipe_opt, inpaint_model, noise_mask_feather)
|
||||
|
||||
image_frames = SEGSPaste.doit(image_frames, enhanced_seg, feather, alpha=255)[0]
|
||||
|
||||
if detailer_hook is not None:
|
||||
detailer_hook.post_paste(image_frames)
|
||||
|
||||
enhanced_segs += enhanced_seg[1]
|
||||
|
||||
new_segs = segs[0], enhanced_segs
|
||||
|
||||
@@ -2,7 +2,7 @@ import configparser
|
||||
import os
|
||||
|
||||
|
||||
version_code = [4, 71]
|
||||
version_code = [4, 72]
|
||||
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||
|
||||
dependency_version = 20
|
||||
|
||||
@@ -63,3 +63,21 @@ class SEGSLabelFilterDetailerHookProvider:
|
||||
def doit(self, preset, labels):
|
||||
hook = hooks.SEGSLabelFilterDetailerHook(labels)
|
||||
return (hook, )
|
||||
|
||||
|
||||
class PreviewDetailerHookProvider:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {"quality": ("INT", {"default": 95, "min": 20, "max": 100})},
|
||||
"hidden": {"unique_id": "UNIQUE_ID"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("DETAILER_HOOK", )
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "ImpactPack/Util"
|
||||
|
||||
def doit(self, quality, unique_id):
|
||||
hook = hooks.PreviewDetailerHook(unique_id, quality)
|
||||
return (hook, )
|
||||
|
||||
+39
-1
@@ -4,7 +4,10 @@ import nodes
|
||||
from impact import utils
|
||||
from . import segs_nodes
|
||||
from thirdparty import noise_nodes
|
||||
|
||||
from server import PromptServer
|
||||
import asyncio
|
||||
import folder_paths
|
||||
import os
|
||||
|
||||
class PixelKSampleHook:
|
||||
cur_step = 0
|
||||
@@ -129,6 +132,9 @@ class DetailerHook(PixelKSampleHook):
|
||||
def post_detection(self, segs):
|
||||
return segs
|
||||
|
||||
def post_paste(self, image):
|
||||
return image
|
||||
|
||||
|
||||
class SimpleDetailerDenoiseSchedulerHook(DetailerHook):
|
||||
def __init__(self, target_denoise):
|
||||
@@ -405,3 +411,35 @@ class SEGSLabelFilterDetailerHook(DetailerHook):
|
||||
|
||||
def post_detection(self, segs):
|
||||
return segs_nodes.SEGSLabelFilter().doit(segs, "", self.labels)[0]
|
||||
|
||||
|
||||
class PreviewDetailerHook(DetailerHook):
|
||||
def __init__(self, node_id, quality):
|
||||
super().__init__()
|
||||
self.node_id = node_id
|
||||
self.quality = quality
|
||||
|
||||
async def send(self, image):
|
||||
if len(image) > 0:
|
||||
image = image[0].unsqueeze(0)
|
||||
img = utils.tensor2pil(image)
|
||||
|
||||
temp_path = os.path.join(folder_paths.get_temp_directory(), 'pvhook')
|
||||
|
||||
if not os.path.exists(temp_path):
|
||||
os.makedirs(temp_path)
|
||||
|
||||
fullpath = os.path.join(temp_path, f"{self.node_id}.webp")
|
||||
img.save(fullpath, quality=self.quality)
|
||||
|
||||
item = {
|
||||
"filename": f"{self.node_id}.webp",
|
||||
"subfolder": 'pvhook',
|
||||
"type": 'temp'
|
||||
}
|
||||
|
||||
PromptServer.instance.send_sync("impact-preview", {'node_id': self.node_id, 'item': item})
|
||||
|
||||
def post_paste(self, image):
|
||||
asyncio.run(self.send(image))
|
||||
return image
|
||||
|
||||
@@ -265,6 +265,9 @@ class DetailerForEach:
|
||||
tensor_paste(image, enhanced_image, (seg.crop_region[0], seg.crop_region[1]), mask)
|
||||
enhanced_list.append(enhanced_image)
|
||||
|
||||
if detailer_hook is not None:
|
||||
detailer_hook.post_paste(image)
|
||||
|
||||
if not (enhanced_image is None):
|
||||
# Convert enhanced_pil_alpha to RGBA mode
|
||||
enhanced_image_alpha = tensor_convert_rgba(enhanced_image)
|
||||
|
||||
Reference in New Issue
Block a user