From b60522aee28dd45a2792f845bbe21fba9ccd80ce Mon Sep 17 00:00:00 2001 From: "Dr.Lt.Data" Date: Fri, 1 Sep 2023 03:37:33 +0900 Subject: [PATCH] feat: SDXL refiner support for Detailer feat: controlnet for SEGS fix: tile size bug --- README.md | 5 + __init__.py | 10 + js/impact-pack.js | 7 +- modules/impact/config.py | 2 +- modules/impact/core.py | 274 ++-- modules/impact/detectors.py | 2 +- modules/impact/impact_pack.py | 138 +- modules/impact/pipe.py | 145 ++- test/detailer-pipe-test-sdxl.json | 1957 +++++++++++++++++++++++++++++ 9 files changed, 2410 insertions(+), 130 deletions(-) create mode 100644 test/detailer-pipe-test-sdxl.json diff --git a/README.md b/README.md index 90cfd8b..d73b3c2 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,9 @@ This custom node helps to conveniently enhance images through Detector, Detailer * As a result, it outputs the `combined_mask`, which is a unified mask, and `batch_masks`, which are multiple masks grouped together in batch form. * While `batch_masks` may not be completely separated, it provides functionality to perform some level of segmentation. * Simple Detector (SEGS) - Operating primarily with `BBOX_DETECTOR`, and with the additional provision of `SAM_MODEL` or `SEGM_DETECTOR`, this node internally generates improved SEGS through mask operations on both *bbox* and *silhouette*. It serves as a convenient tool to simplify a somewhat intricate workflow. + +* ControlNetApply (SEGS) - To apply ControlNet in SEGS, you need to use the Preprocessor Provider node from the Inspire Pack to utilize this node. + * Bitwise(SEGS & SEGS) - Performs a 'bitwise and' operation between two SEGS. * Bitwise(SEGS - SEGS) - Subtracts one SEGS from another. * Bitwise(SEGS & MASK) - Performs a bitwise AND operation between SEGS and MASK. @@ -57,6 +60,8 @@ This custom node helps to conveniently enhance images through Detector, Detailer * FaceDetailer - Easily detects faces and improves them. * FaceDetailer (pipe) - Easily detects faces and improves them (for multipass). +* `FaceDetailer (SDXL/pipe), BasicPipe -> DetailerPipe (SDXL), Edit DetailerPipe (SDXL)` - These are pipe functions used in Detailer for utilizing the refiner model of SDXL. + * SEGSDetailer - Performs detailed work on SEGS without pasting it back onto the original image. * SEGSPaste - Pastes the results of SEGS onto the original image. * If `ref_image_opt` is present, the images contained within SEGS are ignored. Instead, the image within `ref_image_opt` corresponding to the crop area of SEGS is taken and pasted. The size of the image in `ref_image_opt` should be the same as the original image size. diff --git a/__init__.py b/__init__.py index ff7dc63..45d850d 100644 --- a/__init__.py +++ b/__init__.py @@ -121,15 +121,19 @@ NODE_CLASS_MAPPINGS = { "FaceDetailerPipe": FaceDetailerPipe, "ToDetailerPipe": ToDetailerPipe, + "ToDetailerPipeSDXL": ToDetailerPipeSDXL, "FromDetailerPipe": FromDetailerPipe, "FromDetailerPipe_v2": FromDetailerPipe_v2, + "FromDetailerPipeSDXL": FromDetailerPipe_SDXL, "ToBasicPipe": ToBasicPipe, "FromBasicPipe": FromBasicPipe, "FromBasicPipe_v2": FromBasicPipe_v2, "BasicPipeToDetailerPipe": BasicPipeToDetailerPipe, + "BasicPipeToDetailerPipeSDXL": BasicPipeToDetailerPipeSDXL, "DetailerPipeToBasicPipe": DetailerPipeToBasicPipe, "EditBasicPipe": EditBasicPipe, "EditDetailerPipe": EditDetailerPipe, + "EditDetailerPipeSDXL": EditDetailerPipeSDXL, "LatentPixelScale": LatentPixelScale, "PixelKSampleUpscalerProvider": PixelKSampleUpscalerProvider, @@ -163,6 +167,7 @@ NODE_CLASS_MAPPINGS = { "ONNXDetectorSEGS": ONNXDetectorForEach, "ImpactSimpleDetectorSEGS": SimpleDetectorForEach, "ImpactSimpleDetectorSEGSPipe": SimpleDetectorForEachPipe, + "ImpactControlNetApplySEGS": ControlNetApplySEGS, "BboxDetectorCombined_v2": BboxDetectorCombined, "SegmDetectorCombined_v2": SegmDetectorCombined, @@ -240,6 +245,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { "ONNXDetectorSEGS": "ONNX Detector (SEGS)", "ImpactSimpleDetectorSEGS": "Simple Detector (SEGS)", "ImpactSimpleDetectorSEGSPipe": "Simple Detector (SEGS/pipe)", + "ImpactControlNetApplySEGS": "ControlNetApply (SEGS)", "BboxDetectorCombined_v2": "BBOX Detector (combined)", "SegmDetectorCombined_v2": "SEGM Detector (combined)", @@ -260,6 +266,10 @@ NODE_DISPLAY_NAME_MAPPINGS = { "SAMDetectorSegmented": "SAMDetector (segmented)", "FaceDetailerPipe": "FaceDetailer (pipe)", + "FromDetailerPipeSDXL": "FaceDetailer (SDXL/pipe)", + "BasicPipeToDetailerPipeSDXL": "BasicPipe -> DetailerPipe (SDXL)", + "EditDetailerPipeSDXL": "Edit DetailerPipe (SDXL)", + "BasicPipeToDetailerPipe": "BasicPipe -> DetailerPipe", "DetailerPipeToBasicPipe": "DetailerPipe -> BasicPipe", "EditBasicPipe": "Edit BasicPipe", diff --git a/js/impact-pack.js b/js/impact-pack.js index 4a2c8a5..0a85461 100644 --- a/js/impact-pack.js +++ b/js/impact-pack.js @@ -312,7 +312,9 @@ app.registerExtension({ switch(node.comfyClass) { case "ToDetailerPipe": + case "ToDetailerPipeSDXL": case "BasicPipeToDetailerPipe": + case "BasicPipeToDetailerPipeSDXL": case "EditDetailerPipe": case "FaceDetailer": case "DetailerForEach": @@ -354,7 +356,8 @@ app.registerExtension({ }); } - if(node.comfyClass == "ImpactWildcardEncode" || node.comfyClass == "ToDetailerPipe" || node.comfyClass == "EditDetailerPipe" || node.comfyClass == "BasicPipeToDetailerPipe") { + if(node.comfyClass == "ImpactWildcardEncode" || node.comfyClass == "ToDetailerPipe" || node.comfyClass == "ToDetailerPipeSDXL" + || node.comfyClass == "EditDetailerPipe" || node.comfyClass == "BasicPipeToDetailerPipe" || node.comfyClass == "BasicPipeToDetailerPipeSDXL") { node._value = "Select the LoRA to add to the text"; var tbox_id = 0; @@ -367,8 +370,10 @@ app.registerExtension({ break; case "ToDetailerPipe": + case "ToDetailerPipeSDXL": case "EditDetailerPipe": case "BasicPipeToDetailerPipe": + case "BasicPipeToDetailerPipeSDXL": tbox_id = 0; combo_id = 1; break; diff --git a/modules/impact/config.py b/modules/impact/config.py index 0927bc3..c3cd225 100644 --- a/modules/impact/config.py +++ b/modules/impact/config.py @@ -2,7 +2,7 @@ import configparser import os -version = "V3.25.3" +version = "V3.26" dependency_version = 9 diff --git a/modules/impact/core.py b/modules/impact/core.py index c4d8b49..4fc43d3 100644 --- a/modules/impact/core.py +++ b/modules/impact/core.py @@ -14,7 +14,9 @@ import comfy import impact.wildcards as wildcards import math -SEG = namedtuple("SEG", ['cropped_image', 'cropped_mask', 'confidence', 'crop_region', 'bbox', 'label'], + +SEG = namedtuple("SEG", + ['cropped_image', 'cropped_mask', 'confidence', 'crop_region', 'bbox', 'label', 'control_net_wrapper'], defaults=[None]) @@ -37,6 +39,44 @@ def erosion_mask(mask, grow_mask_by): return mask_erosion[:, :, :w, :h].round() +def ksampler_wrapper(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise, + refiner_ratio=None, refiner_model=None, refiner_clip=None, refiner_positive=None, + refiner_negative=None): + if refiner_ratio is None or refiner_model is None or refiner_clip is None or refiner_positive is None or refiner_negative is None: + refined_latent = \ + nodes.KSampler().sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, + denoise)[0] + else: + advanced_steps = math.floor(steps / denoise) + start_at_step = advanced_steps - steps + end_at_step = start_at_step + math.floor(steps * (1.0 - refiner_ratio)) + + print(f"pre: {start_at_step} .. {end_at_step} / {advanced_steps}") + temp_latent = \ + nodes.KSamplerAdvanced().sample(model, "enable", seed, advanced_steps, cfg, sampler_name, scheduler, + positive, negative, latent_image, start_at_step, end_at_step, + "enable")[0] + + if 'noise_mask' in latent_image: + # noise_latent = \ + # nodes.KSamplerAdvanced().sample(refiner_model, "enable", seed, advanced_steps, cfg, sampler_name, + # scheduler, refiner_positive, refiner_negative, latent_image, end_at_step, + # end_at_step, "enable")[0] + + latent_compositor = nodes.NODE_CLASS_MAPPINGS['LatentCompositeMasked']() + temp_latent = \ + latent_compositor.composite(latent_image, temp_latent, 0, 0, False, latent_image['noise_mask'])[0] + + print(f"post: {end_at_step} .. {advanced_steps + 1} / {advanced_steps}") + refined_latent = \ + nodes.KSamplerAdvanced().sample(refiner_model, "disable", seed, advanced_steps, cfg, sampler_name, scheduler, + refiner_positive, refiner_negative, temp_latent, end_at_step, + advanced_steps + 1, + "disable")[0] + + return refined_latent + + class REGIONAL_PROMPT: def __init__(self, mask, sampler): self.mask = mask @@ -76,18 +116,18 @@ def gen_detection_hints_from_mask_area(x, y, mask, threshold, use_negative): plabs = [] # minimum sampling step >= 3 - y_step = max(3, int(mask.shape[0]/20)) - x_step = max(3, int(mask.shape[1]/20)) - + y_step = max(3, int(mask.shape[0] / 20)) + x_step = max(3, int(mask.shape[1] / 20)) + for i in range(0, len(mask), y_step): for j in range(0, len(mask[i]), x_step): if mask[i][j] > threshold: - points.append((x+j, y+i)) + points.append((x + j, y + i)) plabs.append(1) elif use_negative and mask[i][j] == 0: - points.append((x+j, y+i)) + points.append((x + j, y + i)) plabs.append(0) - + return points, plabs @@ -96,20 +136,24 @@ def gen_negative_hints(w, h, x1, y1, x2, y2): nplabs = [] # minimum sampling step >= 3 - y_step = max(3, int(w/20)) - x_step = max(3, int(h/20)) - - for i in range(10, h-10, y_step): - for j in range(10, w-10, x_step): - if not (x1-10 <= j and j <= x2+10 and y1-10 <= i and i <= y2+10): + y_step = max(3, int(w / 20)) + x_step = max(3, int(h / 20)) + + for i in range(10, h - 10, y_step): + for j in range(10, w - 10, x_step): + if not (x1 - 10 <= j and j <= x2 + 10 and y1 - 10 <= i and i <= y2 + 10): npoints.append((j, i)) nplabs.append(0) return npoints, nplabs -def enhance_detail(image, model, clip, vae, guide_size, guide_size_for_bbox, max_size, bbox, seed, steps, cfg, sampler_name, - scheduler, positive, negative, denoise, noise_mask, force_inpaint, wildcard_opt=None, detailer_hook=None): +def enhance_detail(image, model, clip, vae, guide_size, guide_size_for_bbox, max_size, bbox, seed, steps, cfg, + sampler_name, + scheduler, positive, negative, denoise, noise_mask, force_inpaint, wildcard_opt=None, + detailer_hook=None, + refiner_ratio=None, refiner_model=None, refiner_clip=None, refiner_positive=None, + refiner_negative=None, control_net_wrapper=None): if wildcard_opt is not None and wildcard_opt != "": model, _, positive = wildcards.process_with_loras(wildcard_opt, model, clip) @@ -179,7 +223,12 @@ def enhance_detail(image, model, clip, vae, guide_size, guide_size_for_bbox, max if detailer_hook is not None: latent_image = detailer_hook.post_encode(latent_image) - refined_latent = nodes.KSampler().sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise)[0] + if control_net_wrapper is not None: + positive = control_net_wrapper.apply(positive, upscaled_image) + + refined_latent = ksampler_wrapper(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, + latent_image, denoise, + refiner_ratio, refiner_model, refiner_clip, refiner_positive, refiner_negative) # non-latent downscale - latent downscale cause bad quality refined_image = vae.decode(refined_latent['samples']) @@ -239,8 +288,7 @@ def sam_predict(predictor, points, plabs, bbox, threshold): def make_sam_mask(sam_model, segs, image, detection_hint, dilation, - threshold, bbox_expansion, mask_hint_threshold, mask_hint_use_negative): - + threshold, bbox_expansion, mask_hint_threshold, mask_hint_use_negative): if sam_model.is_auto_mode: device = comfy.model_management.get_torch_device() sam_model.to(device=device) @@ -360,8 +408,9 @@ def make_sam_mask(sam_model, segs, image, detection_hint, dilation, return mask -def generate_detection_hints(image,seg,center,detection_hint,dilated_bbox,mask_hint_threshold, use_small_negative, mask_hint_use_negative): +def generate_detection_hints(image, seg, center, detection_hint, dilated_bbox, mask_hint_threshold, use_small_negative, + mask_hint_use_negative): [x1, y1, x2, y2] = dilated_bbox points = [] @@ -407,13 +456,13 @@ def generate_detection_hints(image,seg,center,detection_hint,dilated_bbox,mask_h elif detection_hint == "mask-area": points, plabs = gen_detection_hints_from_mask_area(seg.crop_region[0], seg.crop_region[1], - seg.cropped_mask, - mask_hint_threshold, use_small_negative) + seg.cropped_mask, + mask_hint_threshold, use_small_negative) if mask_hint_use_negative == "Outter": npoints, nplabs = gen_negative_hints(image.shape[0], image.shape[1], - seg.crop_region[0], seg.crop_region[1], - seg.crop_region[2], seg.crop_region[3]) + seg.crop_region[0], seg.crop_region[1], + seg.crop_region[2], seg.crop_region[3]) points += npoints plabs += nplabs @@ -445,7 +494,7 @@ def merge_and_stack_masks(stacked_masks, group_size): merged_masks = [] for i in range(0, num_masks, group_size): - subset_masks = stacked_masks[i:i+group_size] + subset_masks = stacked_masks[i:i + group_size] merged_mask = torch.any(subset_masks, dim=0) merged_masks.append(merged_mask) @@ -464,7 +513,6 @@ def every_three_pick_last(stacked_masks): def make_sam_mask_segmented(sam_model, segs, image, detection_hint, dilation, threshold, bbox_expansion, mask_hint_threshold, mask_hint_use_negative): - if sam_model.is_auto_mode: device = comfy.model_management.get_torch_device() sam_model.to(device=device) @@ -509,7 +557,9 @@ def make_sam_mask_segmented(sam_model, segs, image, detection_hint, dilation, dilated_bbox = [x1, y1, x2, y2] - points, plabs = generate_detection_hints(image, segs[i],center, detection_hint, dilated_bbox, mask_hint_threshold, use_small_negative, mask_hint_use_negative) + points, plabs = generate_detection_hints(image, segs[i], center, detection_hint, dilated_bbox, + mask_hint_threshold, use_small_negative, + mask_hint_use_negative) detected_masks = sam_predict(predictor, points, plabs, dilated_bbox, threshold) @@ -539,7 +589,7 @@ def make_sam_mask_segmented(sam_model, segs, image, detection_hint, dilation, def segs_bitwise_and_mask(segs, mask): if mask is None: print("[SegsBitwiseAndMask] Cannot operate: MASK is empty.") - return ([], ) + return ([],) items = [] @@ -563,7 +613,7 @@ def segs_bitwise_and_mask(segs, mask): def apply_mask_to_each_seg(segs, masks): if masks is None: print("[SegsBitwiseAndMask] Cannot operate: MASK is empty.") - return (segs[0], [], ) + return (segs[0], [],) items = [] @@ -614,9 +664,9 @@ class ONNXDetector: crop_x1, crop_y1, crop_x2, crop_y2, = crop_region # prepare cropped mask - cropped_mask = np.zeros((crop_y2-crop_y1,crop_x2-crop_x1)) - inner_mask = np.ones((y2-y1, x2-x1)) - cropped_mask[y1-crop_y1:y2-crop_y1, x1-crop_x1:x2-crop_x1] = inner_mask + cropped_mask = np.zeros((crop_y2 - crop_y1, crop_x2 - crop_x1)) + inner_mask = np.ones((y2 - y1, x2 - x1)) + cropped_mask[y1 - crop_y1:y2 - crop_y1, x1 - crop_x1:x2 - crop_x1] = inner_mask # make items item = SEG(None, cropped_mask, scores[i], crop_region, item_bbox) @@ -690,10 +740,10 @@ def mask_to_segs(mask, combined, crop_factor, bbox_fill, drop_size=1): for contour in contours: separated_mask = np.zeros_like(mask_i_uint8) cv2.drawContours(separated_mask, [contour], 0, 255, -1) - separated_mask = np.array(separated_mask/255.0).astype(np.float32) + separated_mask = np.array(separated_mask / 255.0).astype(np.float32) x, y, w, h = cv2.boundingRect(contour) - bbox = x, y, x+w, y+h + bbox = x, y, x + w, y + h crop_region = make_crop_region( mask_i.shape[1], mask_i.shape[0], bbox, crop_factor ) @@ -701,8 +751,8 @@ def mask_to_segs(mask, combined, crop_factor, bbox_fill, drop_size=1): if w > drop_size and h > drop_size: cropped_mask = np.array( separated_mask[ - crop_region[1]: crop_region[3], - crop_region[0]: crop_region[2], + crop_region[1]: crop_region[3], + crop_region[0]: crop_region[2], ] ) @@ -791,9 +841,11 @@ class KSamplerWrapper: if hook is not None: model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise = \ - hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise) + hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, + denoise) - return nodes.common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise=denoise)[0] + return nodes.common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, + denoise=denoise)[0] class KSamplerAdvancedWrapper: @@ -802,7 +854,8 @@ class KSamplerAdvancedWrapper: def __init__(self, model, cfg, sampler_name, scheduler, positive, negative): self.params = model, cfg, sampler_name, scheduler, positive, negative - def sample_advanced(self, add_noise, seed, steps, latent_image, start_at_step, end_at_step, return_with_leftover_noise, hook=None): + def sample_advanced(self, add_noise, seed, steps, latent_image, start_at_step, end_at_step, + return_with_leftover_noise, hook=None): model, cfg, sampler_name, scheduler, positive, negative = self.params if hook is not None: @@ -822,7 +875,7 @@ class PixelKSampleHook: def __init__(self): pass - + def set_steps(self, info): self.cur_step, self.total_step = info @@ -835,14 +888,15 @@ class PixelKSampleHook: def post_encode(self, samples): return samples - def pre_ksample(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise): - return model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise + def pre_ksample(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, + denoise): + return model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise class PixelKSampleHookCombine(PixelKSampleHook): hook1 = None hook2 = None - + def __init__(self, hook1, hook2): super().__init__() self.hook1 = hook1 @@ -864,9 +918,11 @@ class PixelKSampleHookCombine(PixelKSampleHook): def pre_ksample(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise): model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise = \ - self.hook1.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise) + self.hook1.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, + upscaled_latent, denoise) - return self.hook2.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise) + return self.hook2.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, + upscaled_latent, denoise) class SimpleCfgScheduleHook(PixelKSampleHook): @@ -875,11 +931,12 @@ class SimpleCfgScheduleHook(PixelKSampleHook): def __init__(self, target_cfg): super().__init__() self.target_cfg = target_cfg - - def pre_ksample(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise): - progress = self.cur_step/self.total_step + + def pre_ksample(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, + denoise): + progress = self.cur_step / self.total_step gap = self.target_cfg - cfg - current_cfg = cfg + gap*progress + current_cfg = cfg + gap * progress return model, seed, steps, current_cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise @@ -890,14 +947,16 @@ class SimpleDenoiseScheduleHook(PixelKSampleHook): super().__init__() self.target_denoise = target_denoise - def pre_ksample(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise): + def pre_ksample(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, + denoise): progress = self.cur_step / self.total_step gap = self.target_denoise - denoise current_denoise = denoise + gap * progress return model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, current_denoise -def latent_upscale_on_pixel_space_shape(samples, scale_method, w, h, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None): +def latent_upscale_on_pixel_space_shape(samples, scale_method, w, h, vae, use_tile=False, tile_size=512, + save_temp_prefix=None, hook=None): pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size) if save_temp_prefix is not None: @@ -911,7 +970,8 @@ def latent_upscale_on_pixel_space_shape(samples, scale_method, w, h, vae, use_ti return vae_encode(vae, pixels, use_tile, hook, tile_size=tile_size) -def latent_upscale_on_pixel_space(samples, scale_method, scale_factor, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None): +def latent_upscale_on_pixel_space(samples, scale_method, scale_factor, vae, use_tile=False, tile_size=512, + save_temp_prefix=None, hook=None): pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size) if save_temp_prefix is not None: @@ -927,7 +987,8 @@ def latent_upscale_on_pixel_space(samples, scale_method, scale_factor, vae, use_ return vae_encode(vae, pixels, use_tile, hook, tile_size=tile_size) -def latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, upscale_model, new_w, new_h, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None): +def latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, upscale_model, new_w, new_h, vae, + use_tile=False, tile_size=512, save_temp_prefix=None, hook=None): pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size) if save_temp_prefix is not None: @@ -950,7 +1011,8 @@ def latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, upscal return vae_encode(vae, pixels, use_tile, hook, tile_size=tile_size) -def latent_upscale_on_pixel_space_with_model(samples, scale_method, upscale_model, scale_factor, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None): +def latent_upscale_on_pixel_space_with_model(samples, scale_method, upscale_model, scale_factor, vae, use_tile=False, + tile_size=512, save_temp_prefix=None, hook=None): pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size) if save_temp_prefix is not None: @@ -988,7 +1050,8 @@ class TwoSamplersForMaskUpscaler: tile_size = 512 def __init__(self, scale_method, sample_schedule, use_tiled_vae, base_sampler, mask_sampler, mask, vae, - full_sampler_opt=None, upscale_model_opt=None, hook_base_opt=None, hook_mask_opt=None, hook_full_opt=None, + full_sampler_opt=None, upscale_model_opt=None, hook_base_opt=None, hook_mask_opt=None, + hook_full_opt=None, tile_size=512): mask = mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])) @@ -1013,7 +1076,8 @@ class TwoSamplersForMaskUpscaler: save_temp_prefix=save_temp_prefix, hook=self.hook_base, tile_size=self.tile_size) else: - upscaled_latent = latent_upscale_on_pixel_space_with_model(samples, scale_method, self.upscale_model, upscale_factor, vae, + upscaled_latent = latent_upscale_on_pixel_space_with_model(samples, scale_method, self.upscale_model, + upscale_factor, vae, use_tile=self.use_tiled_vae, save_temp_prefix=save_temp_prefix, hook=self.hook_mask, tile_size=self.tile_size) @@ -1037,12 +1101,15 @@ class TwoSamplersForMaskUpscaler: if self.upscale_model is None: upscaled_latent = latent_upscale_on_pixel_space_shape(samples, scale_method, w, h, vae, use_tile=self.use_tiled_vae, - save_temp_prefix=save_temp_prefix, hook=self.hook_base, + save_temp_prefix=save_temp_prefix, + hook=self.hook_base, tile_size=self.tile_size) else: - upscaled_latent = latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, self.upscale_model, w, h, vae, + upscaled_latent = latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, self.upscale_model, + w, h, vae, use_tile=self.use_tiled_vae, - save_temp_prefix=save_temp_prefix, hook=self.hook_mask, + save_temp_prefix=save_temp_prefix, + hook=self.hook_mask, tile_size=self.tile_size) return self.do_samples(step_info, base_sampler, mask_sampler, sample_schedule, mask, upscaled_latent) @@ -1070,16 +1137,16 @@ class TwoSamplersForMaskUpscaler: return cur_step == total_step elif sample_schedule == "last2": - return cur_step >= total_step-1 + return cur_step >= total_step - 1 elif sample_schedule == "interleave1+last1": - return cur_step % 2 == 0 or cur_step >= total_step-1 + return cur_step % 2 == 0 or cur_step >= total_step - 1 elif sample_schedule == "interleave2+last1": - return cur_step % 2 == 0 or cur_step >= total_step-1 + return cur_step % 2 == 0 or cur_step >= total_step - 1 elif sample_schedule == "interleave3+last1": - return cur_step % 2 == 0 or cur_step >= total_step-1 + return cur_step % 2 == 0 or cur_step >= total_step - 1 def do_samples(self, step_info, base_sampler, mask_sampler, sample_schedule, mask, upscaled_latent): if self.is_full_sample_time(step_info, sample_schedule): @@ -1092,9 +1159,11 @@ class TwoSamplersForMaskUpscaler: else: print(f"step_info={step_info} / non-full time") # upscale mask - upscaled_mask = F.interpolate(mask, size=(upscaled_latent['samples'].shape[2], upscaled_latent['samples'].shape[3]), + upscaled_mask = F.interpolate(mask, size=( + upscaled_latent['samples'].shape[2], upscaled_latent['samples'].shape[3]), mode='bilinear', align_corners=True) - upscaled_mask = upscaled_mask[:, :, :upscaled_latent['samples'].shape[2], :upscaled_latent['samples'].shape[3]] + upscaled_mask = upscaled_mask[:, :, :upscaled_latent['samples'].shape[2], + :upscaled_latent['samples'].shape[3]] # base sampler upscaled_inv_mask = torch.where(upscaled_mask != 1.0, torch.tensor(1.0), torch.tensor(0.0)) @@ -1140,12 +1209,14 @@ class PixelKSampleUpscaler: upscaled_latent = latent_upscale_on_pixel_space_with_model(samples, scale_method, self.upscale_model, upscale_factor, vae, use_tile=self.use_tiled_vae, - save_temp_prefix=save_temp_prefix, hook=self.hook, + save_temp_prefix=save_temp_prefix, + hook=self.hook, tile_size=self.tile_size) if self.hook is not None: model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise = \ - self.hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise) + self.hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, + upscaled_latent, denoise) refined_latent = nodes.KSampler().sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise)[0] @@ -1163,20 +1234,36 @@ class PixelKSampleUpscaler: save_temp_prefix=save_temp_prefix, hook=self.hook, tile_size=self.tile_size) else: - upscaled_latent = latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, self.upscale_model, w, h, vae, + upscaled_latent = latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, self.upscale_model, + w, h, vae, use_tile=self.use_tiled_vae, - save_temp_prefix=save_temp_prefix, hook=self.hook, + save_temp_prefix=save_temp_prefix, + hook=self.hook, tile_size=self.tile_size) if self.hook is not None: model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise = \ - self.hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise) + self.hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, + upscaled_latent, denoise) refined_latent = nodes.KSampler().sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise)[0] return refined_latent +class ControlNetWrapper: + def __init__(self, control_net, strength, preprocessor): + self.control_net = control_net + self.strength = strength + self.preprocessor = preprocessor + + def apply(self, conditioning, image): + if self.preprocessor is not None: + image = self.preprocessor.apply(image) + + return nodes.ControlNetApply().apply_controlnet(conditioning, self.control_net, image, self.strength)[0] + + # REQUIREMENTS: BlenderNeko/ComfyUI Noise try: class InjectNoiseHook(PixelKSampleHook): @@ -1192,14 +1279,14 @@ try: size = samples['samples'].shape seed = self.cur_step + self.seed from custom_nodes.ComfyUI_Noise.nodes import NoisyLatentImage, InjectNoise - noise = NoisyLatentImage().create_noisy_latents(self.source, seed, size[3]*8, size[2]*8, size[0])[0] + noise = NoisyLatentImage().create_noisy_latents(self.source, seed, size[3] * 8, size[2] * 8, size[0])[0] # inj noise mask = None if 'noise_mask' in samples: mask = samples['noise_mask'] - strength = self.start_strength + (self.end_strength-self.start_strength)*self.cur_step/self.total_step + strength = self.start_strength + (self.end_strength - self.start_strength) * self.cur_step / self.total_step samples = InjectNoise().inject_noise(samples, strength, noise, mask)[0] if mask is not None: @@ -1209,7 +1296,6 @@ try: except: pass - # REQUIREMENTS: BlenderNeko/ComfyUI_TiledKSampler try: class TiledKSamplerWrapper: @@ -1226,10 +1312,14 @@ try: if hook is not None: model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise = \ - hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise) + hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, + denoise) + + return \ + TiledKSampler().sample(model, seed, tile_width, tile_height, tiling_strategy, steps, cfg, sampler_name, + scheduler, + positive, negative, latent_image, denoise)[0] - return TiledKSampler().sample(model, seed, tile_width, tile_height, tiling_strategy, steps, cfg, sampler_name, scheduler, - positive, negative, latent_image, denoise)[0] class PixelTiledKSampleUpscaler: params = None @@ -1239,7 +1329,8 @@ try: is_tiled = True tile_size = 512 - def __init__(self, scale_method, model, vae, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise, + def __init__(self, scale_method, model, vae, seed, steps, cfg, sampler_name, scheduler, positive, negative, + denoise, tile_width, tile_height, tiling_strategy, upscale_model_opt=None, hook_opt=None, tile_size=512): self.params = scale_method, model, vae, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise @@ -1254,8 +1345,10 @@ try: scale_method, model, vae, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise = self.params tile_width, tile_height, tiling_strategy = self.tile_params - return TiledKSampler().sample(model, seed, tile_width, tile_height, tiling_strategy, steps, cfg, sampler_name, scheduler, - positive, negative, latent, denoise)[0] + return \ + TiledKSampler().sample(model, seed, tile_width, tile_height, tiling_strategy, steps, cfg, sampler_name, + scheduler, + positive, negative, latent, denoise)[0] def upscale(self, step_info, samples, upscale_factor, save_temp_prefix=None): scale_method, model, vae, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise = self.params @@ -1265,11 +1358,15 @@ try: if self.upscale_model is None: upscaled_latent = latent_upscale_on_pixel_space(samples, scale_method, upscale_factor, vae, - use_tile=True, save_temp_prefix=save_temp_prefix, hook=self.hook, + use_tile=True, save_temp_prefix=save_temp_prefix, + hook=self.hook, tile_size=self.tile_size) else: - upscaled_latent = latent_upscale_on_pixel_space_with_model(samples, scale_method, self.upscale_model, upscale_factor, vae, - use_tile=True, save_temp_prefix=save_temp_prefix, hook=self.hook, + upscaled_latent = latent_upscale_on_pixel_space_with_model(samples, scale_method, self.upscale_model, + upscale_factor, vae, + use_tile=True, + save_temp_prefix=save_temp_prefix, + hook=self.hook, tile_size=self.tile_size) refined_latent = self.tiled_ksample(upscaled_latent) @@ -1287,9 +1384,12 @@ try: use_tile=True, save_temp_prefix=save_temp_prefix, hook=self.hook, tile_size=self.tile_size) else: - upscaled_latent = latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, self.upscale_model, w, h, vae, - use_tile=True, save_temp_prefix=save_temp_prefix, - hook=self.hook, tile_size=self.tile_size) + upscaled_latent = latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, + self.upscale_model, w, h, vae, + use_tile=True, + save_temp_prefix=save_temp_prefix, + hook=self.hook, + tile_size=self.tile_size) refined_latent = self.tiled_ksample(upscaled_latent) @@ -1297,7 +1397,6 @@ try: except: pass - # REQUIREMENTS: biegert/ComfyUI-CLIPSeg try: class BBoxDetectorBasedOnCLIPSeg: @@ -1361,6 +1460,7 @@ from latent_preview import TAESD, TAESDPreviewerImpl, Latent2RGBPreviewer try: import comfy.latent_formats as latent_formats + def get_previewer(device, latent_format=latent_formats.SD15(), force=False, method=None): previewer = None @@ -1381,7 +1481,8 @@ try: taesd = TAESD(None, taesd_decoder_path).to(device) previewer = TAESDPreviewerImpl(taesd) else: - print("Warning: TAESD previews enabled, but could not find models/vae_approx/{}".format(latent_format.taesd_decoder_name)) + print("Warning: TAESD previews enabled, but could not find models/vae_approx/{}".format( + latent_format.taesd_decoder_name)) if previewer is None: previewer = Latent2RGBPreviewer(latent_format.latent_rgb_factors) @@ -1391,4 +1492,3 @@ except: print(f"#########################################################################") print(f"[ERROR] ComfyUI-Impact-Pack: Please update ComfyUI to the latest version.") print(f"#########################################################################") - diff --git a/modules/impact/detectors.py b/modules/impact/detectors.py index 98d1f77..9e039aa 100644 --- a/modules/impact/detectors.py +++ b/modules/impact/detectors.py @@ -230,7 +230,7 @@ class SimpleDetectorForEachPipe: def doit(self, detailer_pipe, image, bbox_threshold, bbox_dilation, crop_factor, drop_size, sub_threshold, sub_dilation, sub_bbox_expansion, sam_mask_hint_threshold): - model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook = detailer_pipe + model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook, refiner_model, refiner_clip, refiner_positive, refiner_negative = detailer_pipe return SimpleDetectorForEach.detect(bbox_detector, image, bbox_threshold, bbox_dilation, crop_factor, drop_size, sub_threshold, sub_dilation, sub_bbox_expansion, diff --git a/modules/impact/impact_pack.py b/modules/impact/impact_pack.py index 7b77596..47935b2 100644 --- a/modules/impact/impact_pack.py +++ b/modules/impact/impact_pack.py @@ -167,7 +167,11 @@ class SEGSDetailer: "noise_mask": ("BOOLEAN", {"default": True, "label_on": "enabled", "label_off": "disabled"}), "force_inpaint": ("BOOLEAN", {"default": False, "label_on": "enabled", "label_off": "disabled"}), "basic_pipe": ("BASIC_PIPE",), + "refiner_ratio": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0}), }, + "optional": { + "refiner_basic_pipe_opt": ("BASIC_PIPE",), + } } RETURN_TYPES = ("SEGS", ) @@ -177,9 +181,13 @@ class SEGSDetailer: @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): + denoise, noise_mask, force_inpaint, basic_pipe, refiner_ratio=None, refiner_basic_pipe_opt=None): model, clip, vae, positive, negative = basic_pipe + if refiner_basic_pipe_opt is None: + refiner_model, refiner_clip, refiner_positive, refiner_negative = None, None, None, None + else: + refiner_model, refiner_clip, _, refiner_positive, refiner_negative = refiner_basic_pipe_opt new_segs = [] @@ -200,19 +208,26 @@ class SEGSDetailer: enhanced_pil = core.enhance_detail(cropped_image, model, clip, vae, guide_size, guide_size_for, max_size, seg.bbox, seed, steps, cfg, sampler_name, scheduler, - positive, negative, denoise, cropped_mask, force_inpaint) + positive, negative, denoise, cropped_mask, force_inpaint, + refiner_ratio=refiner_ratio, refiner_model=refiner_model, + refiner_clip=refiner_clip, refiner_positive=refiner_positive, refiner_negative=refiner_negative, + control_net_wrapper=seg.control_net_wrapper) + + if enhanced_pil is None: + new_cropped_image = cropped_image + else: + new_cropped_image = pil2numpy(enhanced_pil) - new_cropped_image = pil2numpy(enhanced_pil) new_seg = SEG(new_cropped_image, seg.cropped_mask, seg.confidence, seg.crop_region, seg.bbox, seg.label) new_segs.append(new_seg) return segs[0], new_segs def doit(self, image, segs, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler, - denoise, noise_mask, force_inpaint, basic_pipe): + denoise, noise_mask, force_inpaint, basic_pipe, refiner_ratio=None, refiner_basic_pipe_opt=None): - segs = SEGSDetailer.do_detail(image, segs, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler, - denoise, noise_mask, force_inpaint, basic_pipe) + segs = SEGSDetailer.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, refiner_basic_pipe_opt) return (segs, ) @@ -573,7 +588,8 @@ class DetailerForEach: @staticmethod def do_detail(image, segs, 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, wildcard_opt=None, detailer_hook=None): + positive, negative, denoise, feather, noise_mask, force_inpaint, wildcard_opt=None, detailer_hook=None, + refiner_ratio=None, refiner_model=None, refiner_clip=None, refiner_positive=None, refiner_negative=None): image_pil = tensor2pil(image).convert('RGBA') @@ -599,7 +615,10 @@ class DetailerForEach: enhanced_pil = core.enhance_detail(cropped_image, model, clip, vae, guide_size, guide_size_for_bbox, max_size, seg.bbox, seed, steps, cfg, sampler_name, scheduler, - positive, negative, denoise, cropped_mask, force_inpaint, wildcard_opt, detailer_hook) + positive, negative, denoise, cropped_mask, force_inpaint, wildcard_opt, detailer_hook, + refiner_ratio=refiner_ratio, refiner_model=refiner_model, + refiner_clip=refiner_clip, refiner_positive=refiner_positive, + refiner_negative=refiner_negative, control_net_wrapper=seg.control_net_wrapper) if not (enhanced_pil is None): # don't latent composite-> converting to latent caused poor quality @@ -658,8 +677,12 @@ class DetailerForEachPipe: "force_inpaint": ("BOOLEAN", {"default": True, "label_on": "enabled", "label_off": "disabled"}), "basic_pipe": ("BASIC_PIPE", ), "wildcard": ("STRING", {"multiline": True, "dynamicPrompts": False}), + "refiner_ratio": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0}), }, - "optional": {"detailer_hook": ("DETAILER_HOOK",), } + "optional": { + "detailer_hook": ("DETAILER_HOOK",), + "refiner_basic_pipe_opt": ("BASIC_PIPE",), + } } RETURN_TYPES = ("IMAGE", ) @@ -668,13 +691,21 @@ class DetailerForEachPipe: CATEGORY = "ImpactPack/Detailer" 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, detailer_hook=None): + denoise, feather, noise_mask, force_inpaint, basic_pipe, wildcard, refiner_ratio=None, detailer_hook=None, refiner_basic_pipe_opt=None): model, clip, vae, positive, negative = basic_pipe + + if refiner_basic_pipe_opt is None: + refiner_model, refiner_clip, refiner_positive, refiner_negative = None, None, None, None + else: + refiner_model, refiner_clip, _, refiner_positive, refiner_negative = refiner_basic_pipe_opt + enhanced_img, cropped, cropped_enhanced, cropped_enhanced_alpha = \ DetailerForEach.do_detail(image, segs, model, clip, vae, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise, feather, noise_mask, - force_inpaint, wildcard, detailer_hook) + force_inpaint, wildcard, detailer_hook, + refiner_ratio=refiner_ratio, refiner_model=refiner_model, + refiner_clip=refiner_clip, refiner_positive=refiner_positive, refiner_negative=refiner_negative) return (enhanced_img, ) @@ -982,7 +1013,9 @@ class FaceDetailer: bbox_threshold, bbox_dilation, bbox_crop_factor, sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold, sam_mask_hint_use_negative, drop_size, - bbox_detector, segm_detector=None, sam_model_opt=None, wildcard_opt=None, detailer_hook=None): + bbox_detector, segm_detector=None, sam_model_opt=None, wildcard_opt=None, detailer_hook=None, + refiner_ratio=None, refiner_model=None, refiner_clip=None, refiner_positive=None, refiner_negative=None): + # make default prompt as 'face' if empty prompt for CLIPSeg bbox_detector.setAux('face') segs = bbox_detector.detect(image, bbox_threshold, bbox_dilation, bbox_crop_factor, drop_size) @@ -1003,7 +1036,10 @@ class FaceDetailer: enhanced_img, _, cropped_enhanced, cropped_enhanced_alpha = \ DetailerForEach.do_detail(image, segs, 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, wildcard_opt, detailer_hook) + force_inpaint, wildcard_opt, detailer_hook, + refiner_ratio=refiner_ratio, refiner_model=refiner_model, + refiner_clip=refiner_clip, refiner_positive=refiner_positive, + refiner_negative=refiner_negative) # Mask Generator mask = core.segs_to_combined_mask(segs) @@ -1029,7 +1065,7 @@ class FaceDetailer: sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold, sam_mask_hint_use_negative, drop_size, bbox_detector, segm_detector_opt, sam_model_opt, wildcard, detailer_hook) - pipe = (model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook) + pipe = (model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook, None, None, None, None) return enhanced_img, cropped_enhanced, cropped_enhanced_alpha, mask, pipe @@ -1200,8 +1236,8 @@ class TiledKSamplerProvider: "sampler_name": (comfy.samplers.KSampler.SAMPLERS, ), "scheduler": (comfy.samplers.KSampler.SCHEDULERS, ), "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - "tile_width": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}), - "tile_height": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}), + "tile_width": ("INT", {"default": 512, "min": 320, "max": MAX_RESOLUTION, "step": 64}), + "tile_height": ("INT", {"default": 512, "min": 320, "max": MAX_RESOLUTION, "step": 64}), "tiling_strategy": (["random", "padded", 'simple'], ), "basic_pipe": ("BASIC_PIPE", ) }} @@ -1236,8 +1272,8 @@ class PixelTiledKSampleUpscalerProvider: "positive": ("CONDITIONING", ), "negative": ("CONDITIONING", ), "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - "tile_width": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}), - "tile_height": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}), + "tile_width": ("INT", {"default": 512, "min": 320, "max": MAX_RESOLUTION, "step": 64}), + "tile_height": ("INT", {"default": 512, "min": 320, "max": MAX_RESOLUTION, "step": 64}), "tiling_strategy": (["random", "padded", 'simple'], ), }, "optional": { @@ -1275,8 +1311,8 @@ class PixelTiledKSampleUpscalerProviderPipe: "sampler_name": (comfy.samplers.KSampler.SAMPLERS, ), "scheduler": (comfy.samplers.KSampler.SCHEDULERS, ), "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - "tile_width": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}), - "tile_height": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}), + "tile_width": ("INT", {"default": 512, "min": 320, "max": MAX_RESOLUTION, "step": 64}), + "tile_height": ("INT", {"default": 512, "min": 320, "max": MAX_RESOLUTION, "step": 64}), "tiling_strategy": (["random", "padded", 'simple'], ), "basic_pipe": ("BASIC_PIPE",) }, @@ -1321,7 +1357,7 @@ class PixelKSampleUpscalerProvider: "negative": ("CONDITIONING", ), "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), "use_tiled_vae": ("BOOLEAN", {"default": False, "label_on": "enabled", "label_off": "disabled"}), - "tile_size": ("INT", {"default": 512, "min": 192, "max": 4096, "step": 64}), + "tile_size": ("INT", {"default": 512, "min": 320, "max": 4096, "step": 64}), }, "optional": { "upscale_model_opt": ("UPSCALE_MODEL", ), @@ -1357,7 +1393,7 @@ class PixelKSampleUpscalerProviderPipe(PixelKSampleUpscalerProvider): "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), "use_tiled_vae": ("BOOLEAN", {"default": False, "label_on": "enabled", "label_off": "disabled"}), "basic_pipe": ("BASIC_PIPE",), - "tile_size": ("INT", {"default": 512, "min": 192, "max": 4096, "step": 64}), + "tile_size": ("INT", {"default": 512, "min": 320, "max": 4096, "step": 64}), }, "optional": { "upscale_model_opt": ("UPSCALE_MODEL", ), @@ -1396,7 +1432,7 @@ class TwoSamplersForMaskUpscalerProvider: "mask_sampler": ("KSAMPLER", ), "mask": ("MASK", ), "vae": ("VAE",), - "tile_size": ("INT", {"default": 512, "min": 192, "max": 4096, "step": 64}), + "tile_size": ("INT", {"default": 512, "min": 320, "max": 4096, "step": 64}), }, "optional": { "full_sampler_opt": ("KSAMPLER",), @@ -1438,7 +1474,7 @@ class TwoSamplersForMaskUpscalerProviderPipe: "mask_sampler": ("KSAMPLER", ), "mask": ("MASK", ), "basic_pipe": ("BASIC_PIPE",), - "tile_size": ("INT", {"default": 512, "min": 192, "max": 4096, "step": 64}), + "tile_size": ("INT", {"default": 512, "min": 320, "max": 4096, "step": 64}), }, "optional": { "full_sampler_opt": ("KSAMPLER",), @@ -1590,6 +1626,7 @@ class FaceDetailerPipe: "sam_mask_hint_use_negative": (["False", "Small", "Outter"],), "drop_size": ("INT", {"min": 1, "max": MAX_RESOLUTION, "step": 1, "default": 10}), + "refiner_ratio": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0}), }, } @@ -1603,16 +1640,19 @@ class FaceDetailerPipe: 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, - sam_mask_hint_threshold, sam_mask_hint_use_negative, drop_size): + sam_mask_hint_threshold, sam_mask_hint_use_negative, drop_size, refiner_ratio=None): - model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector, sam_model_opt, detailer_hook = detailer_pipe + model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector, sam_model_opt, detailer_hook, \ + refiner_model, refiner_clip, refiner_positive, refiner_negative = detailer_pipe enhanced_img, cropped_enhanced, cropped_enhanced_alpha, mask = FaceDetailer.enhance_face( image, model, clip, vae, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise, feather, noise_mask, force_inpaint, bbox_threshold, bbox_dilation, bbox_crop_factor, sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold, - sam_mask_hint_use_negative, drop_size, bbox_detector, segm_detector, sam_model_opt, wildcard, detailer_hook) + sam_mask_hint_use_negative, drop_size, bbox_detector, segm_detector, sam_model_opt, wildcard, detailer_hook, + refiner_ratio=refiner_ratio, refiner_model=refiner_model, + refiner_clip=refiner_clip, refiner_positive=refiner_positive, refiner_negative=refiner_negative) if len(cropped_enhanced) == 0: cropped_enhanced = [empty_pil_tensor()] @@ -1663,13 +1703,22 @@ class DetailerForEachTestPipe(DetailerForEachPipe): CATEGORY = "ImpactPack/Detailer" 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, detailer_hook=None): + denoise, feather, noise_mask, force_inpaint, basic_pipe, wildcard, refiner_ratio=None, detailer_hook=None, refiner_basic_pipe_opt=None): model, clip, vae, positive, negative = basic_pipe + + if refiner_basic_pipe_opt is None: + refiner_model, refiner_clip, refiner_positive, refiner_negative = None, None, None, None + else: + refiner_model, refiner_clip, _, refiner_positive, refiner_negative = refiner_basic_pipe_opt + enhanced_img, cropped, cropped_enhanced, cropped_enhanced_alpha = \ DetailerForEach.do_detail(image, segs, model, clip, vae, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise, feather, noise_mask, - force_inpaint, wildcard, detailer_hook) + force_inpaint, wildcard, detailer_hook, + refiner_ratio=refiner_ratio, refiner_model=refiner_model, + refiner_clip=refiner_clip, refiner_positive=refiner_positive, + refiner_negative=refiner_negative) # set fallback image if len(cropped) == 0: @@ -2571,7 +2620,7 @@ class ReencodeLatent: "tile_mode": (["None", "Both", "Decode(input) only", "Encode(output) only"],), "input_vae": ("VAE", ), "output_vae": ("VAE", ), - "tile_size": ("INT", {"default": 512, "min": 192, "max": 4096, "step": 64}), + "tile_size": ("INT", {"default": 512, "min": 320, "max": 4096, "step": 64}), }, } @@ -2716,6 +2765,35 @@ class MakeImageList: return (images, ) +class ControlNetApplySEGS: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "segs": ("SEGS",), + "control_net": ("CONTROL_NET",), + "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + }, + "optional": { + "segs_preprocessor": ("SEGS_PREPROCESSOR",), + } + } + + RETURN_TYPES = ("SEGS",) + FUNCTION = "doit" + + CATEGORY = "ImpactPack/Util" + + def doit(self, segs, control_net, strength, segs_preprocessor=None): + new_segs = [] + + for seg in segs[1]: + control_net_wrapper = impact.core.ControlNetWrapper(control_net, strength, segs_preprocessor) + new_seg = SEG(seg.cropped_image, seg.cropped_mask, seg.confidence, seg.crop_region, seg.bbox, seg.label, control_net_wrapper) + new_segs.append(new_seg) + + return ((segs[0], new_segs), ) + + class StringSelector: @classmethod def INPUT_TYPES(s): diff --git a/modules/impact/pipe.py b/modules/impact/pipe.py index c074a39..bbc20ca 100644 --- a/modules/impact/pipe.py +++ b/modules/impact/pipe.py @@ -27,10 +27,36 @@ class ToDetailerPipe: def doit(self, *args, **kwargs): pipe = (kwargs['model'], kwargs['clip'], kwargs['vae'], kwargs['positive'], kwargs['negative'], kwargs['wildcard'], kwargs['bbox_detector'], - kwargs.get('segm_detector_opt', None), kwargs.get('sam_model_opt', None), kwargs.get('detailer_hook', None)) + kwargs.get('segm_detector_opt', None), kwargs.get('sam_model_opt', None), kwargs.get('detailer_hook', None), + kwargs.get('refiner_model', None), kwargs.get('refiner_clip', None), + kwargs.get('refiner_positive', None), kwargs.get('refiner_negative', None)) return (pipe, ) +class ToDetailerPipeSDXL(ToDetailerPipe): + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "clip": ("CLIP",), + "vae": ("VAE",), + "positive": ("CONDITIONING",), + "negative": ("CONDITIONING",), + "refiner_model": ("MODEL",), + "refiner_clip": ("CLIP",), + "refiner_positive": ("CONDITIONING",), + "refiner_negative": ("CONDITIONING",), + "bbox_detector": ("BBOX_DETECTOR", ), + "wildcard": ("STRING", {"multiline": True, "dynamicPrompts": False}), + "Select to add LoRA": (["Select the LoRA to add to the text"] + folder_paths.get_filename_list("loras"),), + }, + "optional": { + "sam_model_opt": ("SAM_MODEL",), + "segm_detector_opt": ("SEGM_DETECTOR",), + "detailer_hook": ("DETAILER_HOOK",), + }} + + class FromDetailerPipe: @classmethod def INPUT_TYPES(s): @@ -43,7 +69,7 @@ class FromDetailerPipe: CATEGORY = "ImpactPack/Pipe" def doit(self, detailer_pipe): - model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook = detailer_pipe + model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook, _, _, _, _ = detailer_pipe return model, clip, vae, positive, negative, bbox_detector, sam_model_opt, segm_detector_opt, detailer_hook @@ -59,10 +85,26 @@ class FromDetailerPipe_v2: CATEGORY = "ImpactPack/Pipe" def doit(self, detailer_pipe): - model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook = detailer_pipe + model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook, _, _, _, _ = detailer_pipe return detailer_pipe, model, clip, vae, positive, negative, bbox_detector, sam_model_opt, segm_detector_opt, detailer_hook +class FromDetailerPipe_SDXL: + @classmethod + def INPUT_TYPES(s): + return {"required": {"detailer_pipe": ("DETAILER_PIPE",), }, } + + RETURN_TYPES = ("DETAILER_PIPE", "MODEL", "CLIP", "VAE", "CONDITIONING", "CONDITIONING", "BBOX_DETECTOR", "SAM_MODEL", "SEGM_DETECTOR", "DETAILER_HOOK", "MODEL", "CLIP", "CONDITIONING", "CONDITIONING") + RETURN_NAMES = ("detailer_pipe", "model", "clip", "vae", "positive", "negative", "bbox_detector", "sam_model_opt", "segm_detector_opt", "detailer_hook", "refiner_model", "refiner_clip", "refiner_positive", "refiner_negative") + FUNCTION = "doit" + + CATEGORY = "ImpactPack/Pipe" + + def doit(self, detailer_pipe): + model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook, refiner_model, refiner_clip, refiner_positive, refiner_negative = detailer_pipe + return detailer_pipe, model, clip, vae, positive, negative, bbox_detector, sam_model_opt, segm_detector_opt, detailer_hook, refiner_model, refiner_clip, refiner_positive, refiner_negative + + class ToBasicPipe: @classmethod def INPUT_TYPES(s): @@ -148,7 +190,44 @@ class BasicPipeToDetailerPipe: detailer_hook = kwargs.get('detailer_hook', None) model, clip, vae, positive, negative = basic_pipe - pipe = model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook + pipe = model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook, None, None, None, None + return (pipe, ) + + +class BasicPipeToDetailerPipeSDXL: + @classmethod + def INPUT_TYPES(s): + return {"required": {"base_basic_pipe": ("BASIC_PIPE",), + "refiner_basic_pipe": ("BASIC_PIPE",), + "bbox_detector": ("BBOX_DETECTOR", ), + "wildcard": ("STRING", {"multiline": True, "dynamicPrompts": False}), + "Select to add LoRA": (["Select the LoRA to add to the text"] + folder_paths.get_filename_list("loras"),), + }, + "optional": { + "sam_model_opt": ("SAM_MODEL", ), + "segm_detector_opt": ("SEGM_DETECTOR",), + "detailer_hook": ("DETAILER_HOOK",), + }, + } + + RETURN_TYPES = ("DETAILER_PIPE", ) + RETURN_NAMES = ("detailer_pipe", ) + FUNCTION = "doit" + + CATEGORY = "ImpactPack/Pipe" + + def doit(self, *args, **kwargs): + base_basic_pipe = kwargs['base_basic_pipe'] + refiner_basic_pipe = kwargs['refiner_basic_pipe'] + bbox_detector = kwargs['bbox_detector'] + wildcard = kwargs['wildcard'] + sam_model_opt = kwargs.get('sam_model_opt', None) + segm_detector_opt = kwargs.get('segm_detector_opt', None) + detailer_hook = kwargs.get('detailer_hook', None) + + model, clip, vae, positive, negative = base_basic_pipe + refiner_model, refiner_clip, refiner_vae, refiner_positive, refiner_negative = refiner_basic_pipe + pipe = model, clip, vae, positive, negative, wildcard, bbox_detector, segm_detector_opt, sam_model_opt, detailer_hook, refiner_model, refiner_clip, refiner_positive, refiner_negative return (pipe, ) @@ -157,16 +236,17 @@ class DetailerPipeToBasicPipe: def INPUT_TYPES(s): return {"required": {"detailer_pipe": ("DETAILER_PIPE",), }} - RETURN_TYPES = ("BASIC_PIPE", ) - RETURN_NAMES = ("basic_pipe", ) + RETURN_TYPES = ("BASIC_PIPE", "BASIC_PIPE") + RETURN_NAMES = ("base_basic_pipe", "refiner_basic_pipe") FUNCTION = "doit" CATEGORY = "ImpactPack/Pipe" def doit(self, detailer_pipe): - model, clip, vae, positive, negative, _, _, _, _, _ = detailer_pipe + model, clip, vae, positive, negative, _, _, _, _, _, refiner_model, refiner_clip, refiner_positive, refiner_negative = detailer_pipe pipe = model, clip, vae, positive, negative - return (pipe, ) + refiner_pipe = refiner_model, refiner_clip, vae, refiner_positive, refiner_negative + return (pipe, refiner_pipe) class EditBasicPipe: @@ -252,8 +332,12 @@ class EditDetailerPipe: sam_model = kwargs.get('sam_model', None) segm_detector = kwargs.get('segm_detector', None) detailer_hook = kwargs.get('detailer_hook', None) + refiner_model = kwargs.get('refiner_model', None) + refiner_clip = kwargs.get('refiner_clip', None) + refiner_positive = kwargs.get('refiner_positive', None) + refiner_negative = kwargs.get('refiner_negative', None) - res_model, res_clip, res_vae, res_positive, res_negative, res_wildcard, res_bbox_detector, res_segm_detector, res_sam_model, res_detailer_hook = detailer_pipe + res_model, res_clip, res_vae, res_positive, res_negative, res_wildcard, res_bbox_detector, res_segm_detector, res_sam_model, res_detailer_hook, res_refiner_model, res_refiner_clip, res_refiner_positive, res_refiner_negative = detailer_pipe if model is not None: res_model = model @@ -285,6 +369,47 @@ class EditDetailerPipe: if detailer_hook is not None: res_detailer_hook = detailer_hook - pipe = res_model, res_clip, res_vae, res_positive, res_negative, res_wildcard, res_bbox_detector, res_segm_detector, res_sam_model, res_detailer_hook + if refiner_model is not None: + res_refiner_model = refiner_model + + if refiner_clip is not None: + res_refiner_clip = refiner_clip + + if refiner_positive is not None: + res_refiner_positive = refiner_positive + + if refiner_negative is not None: + res_refiner_negative = refiner_negative + + pipe = (res_model, res_clip, res_vae, res_positive, res_negative, res_wildcard, + res_bbox_detector, res_segm_detector, res_sam_model, res_detailer_hook, + res_refiner_model, res_refiner_clip, res_refiner_positive, res_refiner_negative) return (pipe, ) + + +class EditDetailerPipeSDXL(EditDetailerPipe): + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "detailer_pipe": ("DETAILER_PIPE",), + "wildcard": ("STRING", {"multiline": True, "dynamicPrompts": False}), + "Select to add LoRA": (["Select the LoRA to add to the text"] + folder_paths.get_filename_list("loras"),), + }, + "optional": { + "model": ("MODEL",), + "clip": ("CLIP",), + "vae": ("VAE",), + "positive": ("CONDITIONING",), + "negative": ("CONDITIONING",), + "refiner_model": ("MODEL",), + "refiner_clip": ("CLIP",), + "refiner_positive": ("CONDITIONING",), + "refiner_negative": ("CONDITIONING",), + "bbox_detector": ("BBOX_DETECTOR",), + "sam_model": ("SAM_MODEL",), + "segm_detector_opt": ("SEGM_DETECTOR",), + "detailer_hook": ("DETAILER_HOOK",), + }, + } diff --git a/test/detailer-pipe-test-sdxl.json b/test/detailer-pipe-test-sdxl.json new file mode 100644 index 0000000..17f61c7 --- /dev/null +++ b/test/detailer-pipe-test-sdxl.json @@ -0,0 +1,1957 @@ +{ + "last_node_id": 50, + "last_link_id": 113, + "nodes": [ + { + "id": 12, + "type": "CLIPTextEncodeSDXLRefiner", + "pos": [ + 480, + 990 + ], + "size": { + "0": 400, + "1": 200 + }, + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 11 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 13 + ], + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncodeSDXLRefiner" + }, + "widgets_values": [ + 6, + 1024, + 1024, + "ugly, male, western" + ] + }, + { + "id": 14, + "type": "UltralyticsDetectorProvider", + "pos": [ + 963, + 955 + ], + "size": { + "0": 315, + "1": 78 + }, + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "BBOX_DETECTOR", + "type": "BBOX_DETECTOR", + "links": [ + 16 + ], + "shape": 3 + }, + { + "name": "SEGM_DETECTOR", + "type": "SEGM_DETECTOR", + "links": null, + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "UltralyticsDetectorProvider" + }, + "widgets_values": [ + "bbox/face_yolov8m.pt" + ] + }, + { + "id": 18, + "type": "PreviewImage", + "pos": [ + 3270, + 810 + ], + "size": [ + 210, + 246 + ], + "flags": {}, + "order": 21, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 20 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 15, + "type": "SAMLoader", + "pos": [ + 967, + 1086 + ], + "size": { + "0": 315, + "1": 82 + }, + "flags": {}, + "order": 1, + "mode": 0, + "outputs": [ + { + "name": "SAM_MODEL", + "type": "SAM_MODEL", + "links": [ + 17 + ], + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "SAMLoader" + }, + "widgets_values": [ + "sam_vit_b_01ec64.pth", + "CPU" + ] + }, + { + "id": 9, + "type": "CLIPTextEncodeSDXL", + "pos": [ + 640, + -550 + ], + "size": { + "0": 400, + "1": 270 + }, + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 6 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 9 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncodeSDXL" + }, + "widgets_values": [ + 1024, + 1024, + 0, + 0, + 1024, + 1024, + "a closeup photograph of cute girl", + "closeup" + ] + }, + { + "id": 7, + "type": "CheckpointLoaderSimple", + "pos": [ + 60, + -580 + ], + "size": { + "0": 315, + "1": 98 + }, + "flags": {}, + "order": 2, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 1 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 2, + 6, + 7 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [ + 3 + ], + "shape": 3, + "slot_index": 2 + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "SDXL/rundiffusionXL_beta.safetensors" + ] + }, + { + "id": 13, + "type": "LoadImage", + "pos": [ + 257, + 164 + ], + "size": [ + 315, + 314 + ], + "flags": {}, + "order": 3, + "mode": 0, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 15, + 64, + 112 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": [], + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "chunli.png", + "image" + ] + }, + { + "id": 10, + "type": "CLIPTextEncodeSDXL", + "pos": [ + 640, + -230 + ], + "size": { + "0": 400, + "1": 270 + }, + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 7 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 8 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncodeSDXL" + }, + "widgets_values": [ + 1024, + 1024, + 0, + 0, + 1024, + 1024, + "ugly, male", + "ugly, male" + ] + }, + { + "id": 17, + "type": "PreviewImage", + "pos": [ + 3270, + 450 + ], + "size": [ + 210, + 246 + ], + "flags": {}, + "order": 20, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 19 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 8, + "type": "CheckpointLoaderSimple", + "pos": [ + 120, + 590 + ], + "size": { + "0": 315, + "1": 98 + }, + "flags": {}, + "order": 4, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 69 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 5, + 10, + 11 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": null, + "shape": 3, + "slot_index": 2 + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "SDXL/sd_xl_refiner_1.0_0.9vae.safetensors" + ] + }, + { + "id": 11, + "type": "CLIPTextEncodeSDXLRefiner", + "pos": [ + 483, + 738 + ], + "size": { + "0": 400, + "1": 200 + }, + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 10 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 70 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncodeSDXLRefiner" + }, + "widgets_values": [ + 6, + 1024, + 1024, + "high quality" + ] + }, + { + "id": 37, + "type": "PreviewImage", + "pos": [ + 2810, + -280 + ], + "size": [ + 344.0487548828123, + 580.6563842773438 + ], + "flags": {}, + "order": 7, + "mode": 2, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 64 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 16, + "type": "PreviewImage", + "pos": [ + 3200, + -280 + ], + "size": [ + 336.36944162782856, + 585.6206784724828 + ], + "flags": {}, + "order": 18, + "mode": 2, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 18 + } + ], + "title": "SDXL Base only", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 6, + "type": "ToDetailerPipeSDXL", + "pos": [ + 1199, + 379 + ], + "size": { + "0": 400, + "1": 340 + }, + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 1 + }, + { + "name": "clip", + "type": "CLIP", + "link": 2 + }, + { + "name": "vae", + "type": "VAE", + "link": 3 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 9 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 8 + }, + { + "name": "refiner_model", + "type": "MODEL", + "link": 69 + }, + { + "name": "refiner_clip", + "type": "CLIP", + "link": 5, + "slot_index": 6 + }, + { + "name": "refiner_positive", + "type": "CONDITIONING", + "link": 70 + }, + { + "name": "refiner_negative", + "type": "CONDITIONING", + "link": 13, + "slot_index": 8 + }, + { + "name": "bbox_detector", + "type": "BBOX_DETECTOR", + "link": 16, + "slot_index": 9 + }, + { + "name": "sam_model_opt", + "type": "SAM_MODEL", + "link": 17, + "slot_index": 10 + }, + { + "name": "segm_detector_opt", + "type": "SEGM_DETECTOR", + "link": null + }, + { + "name": "detailer_hook", + "type": "DETAILER_HOOK", + "link": null + } + ], + "outputs": [ + { + "name": "detailer_pipe", + "type": "DETAILER_PIPE", + "links": [ + 71 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ToDetailerPipeSDXL" + }, + "widgets_values": [ + "", + "Select the LoRA to add to the text" + ] + }, + { + "id": 40, + "type": "ToDetailerPipeSDXL", + "pos": [ + 2198, + 496 + ], + "size": { + "0": 400, + "1": 340 + }, + "flags": {}, + "order": 13, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 73 + }, + { + "name": "clip", + "type": "CLIP", + "link": 74, + "slot_index": 1 + }, + { + "name": "vae", + "type": "VAE", + "link": 75 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 76, + "slot_index": 3 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 77 + }, + { + "name": "refiner_model", + "type": "MODEL", + "link": 78, + "slot_index": 5 + }, + { + "name": "refiner_clip", + "type": "CLIP", + "link": 79 + }, + { + "name": "refiner_positive", + "type": "CONDITIONING", + "link": 80, + "slot_index": 7 + }, + { + "name": "refiner_negative", + "type": "CONDITIONING", + "link": 81 + }, + { + "name": "bbox_detector", + "type": "BBOX_DETECTOR", + "link": 82 + }, + { + "name": "sam_model_opt", + "type": "SAM_MODEL", + "link": 83, + "slot_index": 10 + }, + { + "name": "segm_detector_opt", + "type": "SEGM_DETECTOR", + "link": 84 + }, + { + "name": "detailer_hook", + "type": "DETAILER_HOOK", + "link": 85, + "slot_index": 12 + } + ], + "outputs": [ + { + "name": "detailer_pipe", + "type": "DETAILER_PIPE", + "links": [], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ToDetailerPipeSDXL" + }, + "widgets_values": [ + "", + "SDXL/person/IU_leejieun_SDXL.safetensors" + ] + }, + { + "id": 38, + "type": "PreviewImage", + "pos": [ + 3590, + -280 + ], + "size": { + "0": 336.36944580078125, + "1": 585.6206665039062 + }, + "flags": {}, + "order": 19, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 67 + } + ], + "title": "SDXL Base + Refiner", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 41, + "type": "BasicPipeToDetailerPipeSDXL", + "pos": [ + 2160, + 1010 + ], + "size": { + "0": 405.5999755859375, + "1": 200 + }, + "flags": {}, + "order": 15, + "mode": 0, + "inputs": [ + { + "name": "base_basic_pipe", + "type": "BASIC_PIPE", + "link": 87 + }, + { + "name": "refiner_basic_pipe", + "type": "BASIC_PIPE", + "link": 88 + }, + { + "name": "bbox_detector", + "type": "BBOX_DETECTOR", + "link": 89 + }, + { + "name": "sam_model_opt", + "type": "SAM_MODEL", + "link": 90, + "slot_index": 3 + }, + { + "name": "segm_detector_opt", + "type": "SEGM_DETECTOR", + "link": 91, + "slot_index": 4 + }, + { + "name": "detailer_hook", + "type": "DETAILER_HOOK", + "link": 92, + "slot_index": 5 + } + ], + "outputs": [ + { + "name": "detailer_pipe", + "type": "DETAILER_PIPE", + "links": [ + 86, + 110 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "BasicPipeToDetailerPipeSDXL" + }, + "widgets_values": [ + "", + "Select the LoRA to add to the text" + ] + }, + { + "id": 42, + "type": "ToBasicPipe", + "pos": [ + 1790, + 940 + ], + "size": { + "0": 241.79998779296875, + "1": 106 + }, + "flags": {}, + "order": 12, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 93 + }, + { + "name": "clip", + "type": "CLIP", + "link": 94, + "slot_index": 1 + }, + { + "name": "vae", + "type": "VAE", + "link": 95, + "slot_index": 2 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 97, + "slot_index": 3 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 98, + "slot_index": 4 + } + ], + "outputs": [ + { + "name": "basic_pipe", + "type": "BASIC_PIPE", + "links": [ + 87, + 107 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ToBasicPipe" + } + }, + { + "id": 39, + "type": "FromDetailerPipe_SDXL", + "pos": [ + 1704, + 480 + ], + "size": [ + 269.43489990234366, + 286 + ], + "flags": {}, + "order": 11, + "mode": 0, + "inputs": [ + { + "name": "detailer_pipe", + "type": "DETAILER_PIPE", + "link": 71 + } + ], + "outputs": [ + { + "name": "detailer_pipe", + "type": "DETAILER_PIPE", + "links": null, + "shape": 3, + "slot_index": 0 + }, + { + "name": "model", + "type": "MODEL", + "links": [ + 73, + 93 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "clip", + "type": "CLIP", + "links": [ + 74, + 94 + ], + "shape": 3 + }, + { + "name": "vae", + "type": "VAE", + "links": [ + 75, + 95, + 96 + ], + "shape": 3, + "slot_index": 3 + }, + { + "name": "positive", + "type": "CONDITIONING", + "links": [ + 76, + 97 + ], + "shape": 3 + }, + { + "name": "negative", + "type": "CONDITIONING", + "links": [ + 77, + 98 + ], + "shape": 3, + "slot_index": 5 + }, + { + "name": "bbox_detector", + "type": "BBOX_DETECTOR", + "links": [ + 82, + 89 + ], + "shape": 3, + "slot_index": 6 + }, + { + "name": "sam_model_opt", + "type": "SAM_MODEL", + "links": [ + 83, + 90 + ], + "shape": 3, + "slot_index": 7 + }, + { + "name": "segm_detector_opt", + "type": "SEGM_DETECTOR", + "links": [ + 84, + 91 + ], + "shape": 3, + "slot_index": 8 + }, + { + "name": "detailer_hook", + "type": "DETAILER_HOOK", + "links": [ + 85, + 92 + ], + "shape": 3 + }, + { + "name": "refiner_model", + "type": "MODEL", + "links": [ + 78, + 99 + ], + "shape": 3, + "slot_index": 10 + }, + { + "name": "refiner_clip", + "type": "CLIP", + "links": [ + 79, + 100 + ], + "shape": 3, + "slot_index": 11 + }, + { + "name": "refiner_positive", + "type": "CONDITIONING", + "links": [ + 80, + 101 + ], + "shape": 3, + "slot_index": 12 + }, + { + "name": "refiner_negative", + "type": "CONDITIONING", + "links": [ + 81, + 102 + ], + "shape": 3, + "slot_index": 13 + } + ], + "properties": { + "Node name for S&R": "FromDetailerPipe_SDXL" + } + }, + { + "id": 44, + "type": "FaceDetailerPipe", + "pos": [ + 3565, + 427 + ], + "size": { + "0": 456, + "1": 902 + }, + "flags": {}, + "order": 22, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 104, + "slot_index": 0 + }, + { + "name": "detailer_pipe", + "type": "DETAILER_PIPE", + "link": 103 + } + ], + "outputs": [ + { + "name": "image", + "type": "IMAGE", + "links": [], + "shape": 3, + "slot_index": 0 + }, + { + "name": "cropped_refined", + "type": "IMAGE", + "links": [], + "shape": 6, + "slot_index": 1 + }, + { + "name": "cropped_enhanced_alpha", + "type": "IMAGE", + "links": [ + 105 + ], + "shape": 6, + "slot_index": 2 + }, + { + "name": "mask", + "type": "MASK", + "links": null, + "shape": 3 + }, + { + "name": "detailer_pipe", + "type": "DETAILER_PIPE", + "links": null, + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "FaceDetailerPipe" + }, + "widgets_values": [ + 1024, + false, + 768, + 104033248204033, + "fixed", + 30, + 8, + "euler", + "normal", + 0.5, + 5, + true, + true, + 0.6, + 30, + 3, + "center-1", + 30, + 0.93, + 0, + 0.7, + "False", + 10, + 0.1 + ] + }, + { + "id": 45, + "type": "PreviewImage", + "pos": [ + 4109.76494140625, + 483.81650390625 + ], + "size": [ + 210, + 246 + ], + "flags": {}, + "order": 24, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 105 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 1, + "type": "FaceDetailerPipe", + "pos": [ + 2720, + 430 + ], + "size": [ + 456, + 902 + ], + "flags": {}, + "order": 16, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 15, + "slot_index": 0 + }, + { + "name": "detailer_pipe", + "type": "DETAILER_PIPE", + "link": 86 + } + ], + "outputs": [ + { + "name": "image", + "type": "IMAGE", + "links": [ + 18, + 67, + 104, + 106 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "cropped_refined", + "type": "IMAGE", + "links": [ + 19 + ], + "shape": 6, + "slot_index": 1 + }, + { + "name": "cropped_enhanced_alpha", + "type": "IMAGE", + "links": [ + 20 + ], + "shape": 6, + "slot_index": 2 + }, + { + "name": "mask", + "type": "MASK", + "links": null, + "shape": 3 + }, + { + "name": "detailer_pipe", + "type": "DETAILER_PIPE", + "links": [ + 103 + ], + "shape": 3, + "slot_index": 4 + } + ], + "properties": { + "Node name for S&R": "FaceDetailerPipe" + }, + "widgets_values": [ + 1024, + false, + 768, + 104033248204033, + "fixed", + 30, + 8, + "euler", + "normal", + 0.5, + 5, + true, + true, + 0.6, + 30, + 3, + "center-1", + 30, + 0.93, + 0, + 0.7, + "False", + 10, + 0.1 + ] + }, + { + "id": 43, + "type": "ToBasicPipe", + "pos": [ + 1790, + 1130 + ], + "size": { + "0": 241.79998779296875, + "1": 106 + }, + "flags": {}, + "order": 14, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 99 + }, + { + "name": "clip", + "type": "CLIP", + "link": 100 + }, + { + "name": "vae", + "type": "VAE", + "link": 96, + "slot_index": 2 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 101 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 102 + } + ], + "outputs": [ + { + "name": "basic_pipe", + "type": "BASIC_PIPE", + "links": [ + 88, + 108 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ToBasicPipe" + } + }, + { + "id": 49, + "type": "ImpactSimpleDetectorSEGSPipe", + "pos": [ + 2236.375298828125, + 1520.8711416015626 + ], + "size": { + "0": 315, + "1": 246 + }, + "flags": {}, + "order": 17, + "mode": 0, + "inputs": [ + { + "name": "detailer_pipe", + "type": "DETAILER_PIPE", + "link": 110, + "slot_index": 0 + }, + { + "name": "image", + "type": "IMAGE", + "link": 112, + "slot_index": 1 + } + ], + "outputs": [ + { + "name": "SEGS", + "type": "SEGS", + "links": [ + 111 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImpactSimpleDetectorSEGSPipe" + }, + "widgets_values": [ + 0.5, + 0, + 3, + 10, + 0.5, + 0, + 0, + 0.7 + ] + }, + { + "id": 47, + "type": "DetailerForEachPipe", + "pos": [ + 2725, + 1448 + ], + "size": [ + 456.56388211841386, + 559.115060945277 + ], + "flags": {}, + "order": 23, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 106 + }, + { + "name": "segs", + "type": "SEGS", + "link": 111, + "slot_index": 1 + }, + { + "name": "basic_pipe", + "type": "BASIC_PIPE", + "link": 107, + "slot_index": 2 + }, + { + "name": "detailer_hook", + "type": "DETAILER_HOOK", + "link": null + }, + { + "name": "refiner_basic_pipe_opt", + "type": "BASIC_PIPE", + "link": 108 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 113 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "DetailerForEachPipe" + }, + "widgets_values": [ + 256, + true, + 768, + 450265819682234, + "fixed", + 20, + 8, + "euler", + "normal", + 0.5, + 5, + true, + true, + "", + 0.2 + ] + }, + { + "id": 50, + "type": "PreviewImage", + "pos": [ + 3448.7228955078117, + 1463.962194335937 + ], + "size": [ + 210, + 246 + ], + "flags": {}, + "order": 25, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 113 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + } + ], + "links": [ + [ + 1, + 7, + 0, + 6, + 0, + "MODEL" + ], + [ + 2, + 7, + 1, + 6, + 1, + "CLIP" + ], + [ + 3, + 7, + 2, + 6, + 2, + "VAE" + ], + [ + 5, + 8, + 1, + 6, + 6, + "CLIP" + ], + [ + 6, + 7, + 1, + 9, + 0, + "CLIP" + ], + [ + 7, + 7, + 1, + 10, + 0, + "CLIP" + ], + [ + 8, + 10, + 0, + 6, + 4, + "CONDITIONING" + ], + [ + 9, + 9, + 0, + 6, + 3, + "CONDITIONING" + ], + [ + 10, + 8, + 1, + 11, + 0, + "CLIP" + ], + [ + 11, + 8, + 1, + 12, + 0, + "CLIP" + ], + [ + 13, + 12, + 0, + 6, + 8, + "CONDITIONING" + ], + [ + 15, + 13, + 0, + 1, + 0, + "IMAGE" + ], + [ + 16, + 14, + 0, + 6, + 9, + "BBOX_DETECTOR" + ], + [ + 17, + 15, + 0, + 6, + 10, + "SAM_MODEL" + ], + [ + 18, + 1, + 0, + 16, + 0, + "IMAGE" + ], + [ + 19, + 1, + 1, + 17, + 0, + "IMAGE" + ], + [ + 20, + 1, + 2, + 18, + 0, + "IMAGE" + ], + [ + 64, + 13, + 0, + 37, + 0, + "IMAGE" + ], + [ + 67, + 1, + 0, + 38, + 0, + "IMAGE" + ], + [ + 69, + 8, + 0, + 6, + 5, + "MODEL" + ], + [ + 70, + 11, + 0, + 6, + 7, + "CONDITIONING" + ], + [ + 71, + 6, + 0, + 39, + 0, + "DETAILER_PIPE" + ], + [ + 73, + 39, + 1, + 40, + 0, + "MODEL" + ], + [ + 74, + 39, + 2, + 40, + 1, + "CLIP" + ], + [ + 75, + 39, + 3, + 40, + 2, + "VAE" + ], + [ + 76, + 39, + 4, + 40, + 3, + "CONDITIONING" + ], + [ + 77, + 39, + 5, + 40, + 4, + "CONDITIONING" + ], + [ + 78, + 39, + 10, + 40, + 5, + "MODEL" + ], + [ + 79, + 39, + 11, + 40, + 6, + "CLIP" + ], + [ + 80, + 39, + 12, + 40, + 7, + "CONDITIONING" + ], + [ + 81, + 39, + 13, + 40, + 8, + "CONDITIONING" + ], + [ + 82, + 39, + 6, + 40, + 9, + "BBOX_DETECTOR" + ], + [ + 83, + 39, + 7, + 40, + 10, + "SAM_MODEL" + ], + [ + 84, + 39, + 8, + 40, + 11, + "SEGM_DETECTOR" + ], + [ + 85, + 39, + 9, + 40, + 12, + "DETAILER_HOOK" + ], + [ + 86, + 41, + 0, + 1, + 1, + "DETAILER_PIPE" + ], + [ + 87, + 42, + 0, + 41, + 0, + "BASIC_PIPE" + ], + [ + 88, + 43, + 0, + 41, + 1, + "BASIC_PIPE" + ], + [ + 89, + 39, + 6, + 41, + 2, + "BBOX_DETECTOR" + ], + [ + 90, + 39, + 7, + 41, + 3, + "SAM_MODEL" + ], + [ + 91, + 39, + 8, + 41, + 4, + "SEGM_DETECTOR" + ], + [ + 92, + 39, + 9, + 41, + 5, + "DETAILER_HOOK" + ], + [ + 93, + 39, + 1, + 42, + 0, + "MODEL" + ], + [ + 94, + 39, + 2, + 42, + 1, + "CLIP" + ], + [ + 95, + 39, + 3, + 42, + 2, + "VAE" + ], + [ + 96, + 39, + 3, + 43, + 2, + "VAE" + ], + [ + 97, + 39, + 4, + 42, + 3, + "CONDITIONING" + ], + [ + 98, + 39, + 5, + 42, + 4, + "CONDITIONING" + ], + [ + 99, + 39, + 10, + 43, + 0, + "MODEL" + ], + [ + 100, + 39, + 11, + 43, + 1, + "CLIP" + ], + [ + 101, + 39, + 12, + 43, + 3, + "CONDITIONING" + ], + [ + 102, + 39, + 13, + 43, + 4, + "CONDITIONING" + ], + [ + 103, + 1, + 4, + 44, + 1, + "DETAILER_PIPE" + ], + [ + 104, + 1, + 0, + 44, + 0, + "IMAGE" + ], + [ + 105, + 44, + 2, + 45, + 0, + "IMAGE" + ], + [ + 106, + 1, + 0, + 47, + 0, + "IMAGE" + ], + [ + 107, + 42, + 0, + 47, + 2, + "BASIC_PIPE" + ], + [ + 108, + 43, + 0, + 47, + 4, + "BASIC_PIPE" + ], + [ + 110, + 41, + 0, + 49, + 0, + "DETAILER_PIPE" + ], + [ + 111, + 49, + 0, + 47, + 1, + "SEGS" + ], + [ + 112, + 13, + 0, + 49, + 1, + "IMAGE" + ], + [ + 113, + 47, + 0, + 50, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": {}, + "version": 0.4 +} \ No newline at end of file