feat: PreviewDetailerHook

This commit is contained in:
Dr.Lt.Data
2024-02-01 16:54:18 +09:00
parent a823348392
commit 8afba2e654
10 changed files with 113 additions and 35 deletions
+2 -1
View File
@@ -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.
+1
View File
@@ -180,6 +180,7 @@ NODE_CLASS_MAPPINGS = {
"NoiseInjectionHookProvider": NoiseInjectionHookProvider,
"UnsamplerHookProvider": UnsamplerHookProvider,
"CoreMLDetailerHookProvider": CoreMLDetailerHookProvider,
"PreviewDetailerHookProvider": PreviewDetailerHookProvider,
"DetailerHookCombine": DetailerHookCombine,
"NoiseInjectionDetailerHookProvider": NoiseInjectionDetailerHookProvider,
+8 -8
View File
@@ -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;
+13
View File
@@ -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
View File
@@ -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({
}
});
}
}
})
}
})
+4
View File
@@ -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
+1 -1
View File
@@ -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
+18
View File
@@ -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
View File
@@ -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
+3
View File
@@ -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)