Upgrade to V2.0

Overhaul node structure.
This commit is contained in:
Dr.Lt.Data
2023-05-08 14:03:12 +09:00
parent abb4e864e1
commit 8ee041fcff
67 changed files with 2165 additions and 13829 deletions
+67 -52
View File
@@ -1,30 +1,56 @@
# ComfyUI-Impact-Pack
This custom node helps to conveniently enhance images through Detector, Detailer, Upscaler, Pipe, and more.
## Custom nodes pack for ComfyUI
# Features
* MMDetLoader - Load MMDet model
* SAMLoader - Load SAM model
* ONNXLoader - Load ONNX model
* SegmDetectorCombined - Detect segmentation and return mask from input image.
* BboxDetectorCombined - Detect bbox(bounding box) and return mask from input image.
* SamDetectorCombined - Using the technology of SAM, extract the segment at the location indicated by the input SEGS on the input image, and output it as a unified mask.
* BitwiseAndMask - Perform 'bitwise and' operations between 2 masks
* SubtractMask - Perform subtract operations between 2 masks
* SegmDetectorForEach - Detect segmentation and return SEGS from input image.
* BboxDetectorForEach - Detect bbox(bounding box) and return SEGS from input image.
* ONNXDetectorForeach - Using the ONNX model, identify the bbox and retrieve the SEGS from the input image
* DetailerForEach - Refine image rely on SEGS.
* DetailerForEachDebug - Refine image rely on SEGS. Additionally, you can monitor cropped image and refined image of cropped image.
* MMDetDetectorProvider - Load MMDet model to provide BBOX_DETECTOR, SEGM_DETECTOR
* ONNXDetectorProvider - Load ONNX model to provide SEGM_DETECTOR
* CLIPSegDetectorProvider - CLIPSeg wrapper to provide BBOX_DETECTOR
* SEGM Detector (combined) - Detect segmentation and return mask from input image.
* BBOX Detector (combined) - Detect bbox(bounding box) and return mask from input image.
* SAMDetector (combined) - Using the technology of SAM, extract the segment at the location indicated by the input SEGS on the input image, and output it as a unified mask.
* Bitwise(SEGS & SEGS) - Perform 'bitwise and' operations between 2 SEGS.
* Bitwise(SEGS - SEGS) - Perform subtract operations between 2 SEGS.
* Bitwise(SEGS & MASK) - Perform a bitwise AND operation on SEGS and MASK.
* Bitwise(MASK & MASK) - Perform 'bitwise and' operations between 2 masks
* Bitwise(MASK - MASK) - Perform subtract operations between 2 masks
* SEGM Detector (SEGS) - Detect segmentation and return SEGS from input image.
* BBOX Detector (SEGS) - Detect bbox(bounding box) and return SEGS from input image.
* ONNX Detector (SEGS) - Using the ONNX model, identify the bbox and retrieve the SEGS from the input image
* Detailer (SEGS) - Refine image rely on SEGS.
* DetailerDebug (SEGS) - Refine image rely on SEGS. Additionally, you can monitor cropped image and refined image of cropped image.
* The 'DetailerForEach' and 'DetailerForEachDebug' now support an 'external_seed' that is obtained from the Seed node on the [WAS suite](https://github.com/WASasquatch/was-node-suite-comfyui)
* To prevent the regeneration caused by the seed that does not change every time when using 'external_seed', please disable the 'seed random generate' option in the 'Detailer...' node
* BitwiseAndMaskForEach - Perform 'bitwise and' operations between 2 SEGS.
* BitwiseAndMaskForEach - Perform subtract operations between 2 SEGS.
* Segs & Masks - Perform a bitwise AND operation on SEGS and MASK.
* MaskToSegs - This node generates SEGS based on the mask.
* MASK to SEGS - This node generates SEGS based on the mask.
* ToBinaryMask - This node separates the mask generated with alpha values between 0 and 255 into 0 and 255. The non-zero parts are always set to 255.
* EmptySEGS - This node provides a empty SEGS.
* MaskPainter - This node provides a feature to draw masks.
* FaceDetailer - This is a node that can easily detect faces and improve them.
* FaceDetailer (pipe) - This is a node that can easily detect faces and improve them. (for multipass)
* Pipe nodes
* ToDetailerPipe, FromDetailerPipe - These nodes are used to bundle multiple inputs used in the detailer, such as models and vae, ..., into a single DETAILER_PIPE or extract the elements that are bundled in the DETAILER_PIPE.
* ToBasicPipe, FromBasicPipe - These nodes are used to bundle model, clip, vae, positive conditioning, and negative conditioning into a single BASIC_PIPE, or extract each element from the BASIC_PIPE.
* EditBasicPipe, EditDetailerPipe - These nodes are used to replace some elements in BASIC_PIPE or DETAILER_PIPE.
* Latent Scale (on Pixel Space) - This node converts latent to pixel space, upscales it, and then converts it back to latent.
* If upscale_model_opt is provided, it uses the model to upscale the pixel and then downscales it using the interpolation method provided in scale_method to the target resolution.
* PixelKSampleUpscalerProvider - An upscaler is provided that converts latent to pixels using VAEDecode, performs upscaling, converts back to latent using VAEEncode, and then performs k-sampling. This upscaler can be attached to nodes such as 'Iterative Upscale' for use.
* Similar to 'Latent Scale (on Pixel Space)', if upscale_model_opt is provided, it performs pixel upscaling using the model.
* Iterative Upscale (Latent) - 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.
* Iterative Upscale (Image) - The upscaler takes the input upscaler and splits the scale_factor into steps, then iteratively performs upscaling. This takes image as input and outputs image as the result.
* Internally, this node uses 'Iterative Upscale (Latent)'.
# Depercated
* The following nodes have been kept only for compatibility with existing workflows, and are no longer supported. Please replace them with new nodes.
* MMDetLoader -> MMDetDetectorProvider
* SegsMaskCombine -> SEGS to MASK (combined)
* BboxDetectorForEach -> BBOX Detector (SEGS)
* SegmDetectorForEach -> SEGM Detector (SEGS)
* BboxDetectorCombined -> BBOX Detector (combined)
* SegmDetectorCombined -> SEGM Detector (combined)
# Installation
@@ -61,65 +87,52 @@
# How to use (DDetailer feature)
#### 1. Basic auto face detection and refine exapmle.
![example](misc/simple.png)
![simple](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/simple.png)
* The face that has been damaged due to low resolution is restored with high resolution by generating and synthesizing it, in order to restore the details.
* The FaceDetailer node is a combination of a Detector node for face detection and a Detailer node for image enhancement. See the [Advanced Tutorial](tutorial/advanced.md) for a more detailed explanation.
* The FaceDetailer node is a combination of a Detector node for face detection and a Detailer node for image enhancement. See the [Advanced Tutorial](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/tutorial/advanced.md) for a more detailed explanation.
* Pass the MMDetLoader 's bbox model and the detection model loaded by SAMLoader to FaceDetailer . Since it performs the function of KSampler for image enhancement, it overlaps with KSampler's options.
* The MASK output of FaceDetailer provides a visualization of where the detected and enhanced areas are.
![example](misc/simple-original.png) ![example](misc/simple-refined.png)
![simple-orig](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/simple-original.png) ![simple-refined](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/simple-refined.png)
* You can see that the face in the image on the left has increased detail as in the image on the right.
#### 2. 2Pass refine (restore a severely damaged face)
![2pass-workflow-example](misc/2pass-simple.png)
![2pass-workflow-example](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/2pass-simple.png)
* Although two FaceDetailers can be attached together for a 2-pass configuration, various common inputs used in KSampler can be passed through DETAILER_PIPE, so FaceDetailerPipe can be used to configure easily.
* In 1pass, only rough outline recovery is required, so restore with a reasonable resolution and low options. However, if you increase the dilation at this time, not only the face but also the surrounding parts are included in the recovery range, so it is useful when you need to reshape the face other than the facial part.
![2pass-example-original](misc/2pass-original.png) ![2pass-example-middle](misc/2pass-1pass.png) ![2pass-example-result](misc/2pass-2pass.png)
![2pass-example-original](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/2pass-original.png) ![2pass-example-middle](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/2pass-1pass.png) ![2pass-example-result](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/2pass-2pass.png)
* In the first stage, the severely damaged face is restored to some extent, and in the second stage, the details are restored
#### 3. Face Bbox(bounding box) + Person silhouette segmantation (prevent distortion of the background.)
![combination-workflow-example](misc/combination.png)
![combination-example-original](misc/combination-original.png) ![combination-example-refined](misc/combination-refined.png)
#### 3. Face Bbox(bounding box) + Person silhouette segmentation (prevent distortion of the background.)
![combination-workflow-example](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/combination.png)
![combination-example-original](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/combination-original.png) ![combination-example-refined](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/combination-refined.png)
* Facial synthesis that emphasizes details is delicately aligned with the contours of the face, and it can be observed that it does not affect the image outside of the face.
* The BBoxDetectorForEach node is used to detect faces, and the SAMDetectorCombined node is used to find the segment related to the detected face. By using the Segs & Mask node with the two masks obtained in this way, an accurate mask that intersects based on segs can be generated. If this generated mask is input to the DetailerForEach node, only the target area can be created in high resolution from the image and then composited.
#### Mask feature
#### 4. Iterative Upscale
![upscale-workflow-example](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/upscale-workflow.png)
* The IterativeUpscale node is a node that enlarges an image/latent by a scale_factor. In this process, the upscale is carried out progressively by dividing it into steps.
* IterativeUpscale takes an Upscaler as an input, similar to a plugin, and uses it during each iteration. PixelKSampleUpscalerProvider is an Upscaler that converts the latent representation to pixel space and applies ksampling.
* The upscale_model_opt is an optional parameter that determines whether to use the upscale function of the model base if available. Using the upscale function of the model base can significantly reduce the number of iterative steps required. If an x2 upscaler is used, the image/latent is first upscaled by a factor of 2 and then downscaled to the target scale at each step before further processing is done.
![mask-workflow-example](misc/mask.png)
* The following image is an image of 304x512 pixels and the same image scaled up to three times its original size using IterativeUpscale.
* SEGS generated by the ...Detector nodes can also be converted to a MASK using nodes such as SegsCombineMask and used accordingly.
![combination-example-original](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/upscale-original.png) ![combination-example-refined](https://github.com/ltdrdata/ComfyUI-extension-tutorials/raw/Main/ComfyUI-Impact-Pack/images/upscale-3x.png)
#### SAMDetection Application
![sam-workflow-example](misc/sam.png)
![sam-example-original](misc/sam-original.png) ![sam-example-masked](misc/sam-masked.png) ![sam-example-result](misc/sam-result.png)
* By using the segmentation feature of SAM, it is possible to automatically generate the optimal mask and apply it to areas other than the face. The image on the left is the original image, the middle image is the result of applying a mask to the alpha channel, and the image on the right is the final result.
* The "detection_hint" in "SAMDetectorCombined" is a specifier that indicates which points should be included in the segmentation when performing segmentation. "center-1" specifies one point in the center of the mask, "horizontal-2" specifies two points on the center horizontal line, "vertical-2" specifies two points on the center vertical line, "rectangle-4" specifies four points in a rectangular shape inside the mask, and "diamond-4" specifies four points in a diamond shape centered around the center point.
* Unlike in face detection, for non-rigid objects, the center point may not always be the segmentation area, so be careful not to assume that the center point is always the segmentation area.
#### Mask Painter
![example](misc/maskpainter1.png)
* Click "Edit mask" button
* **Don't connect to 'mask_image' input**
![example](misc/maskpainter2.png)
* You can draw mask on Mask Painter
* Currently, this editor only provides basic functionalities
<img src="misc/maskpainter-original.png" width=352 height=512/> <img src="misc/maskpainter-result.png" width=352 height=512/>
* When used together, SAMDetector and MaskPainter can be used to enhance specific elements of an image.
# Others Tutorials
* [Advanced Tutorial](tutorial/advanced.md)
* [Mask Pointer](tutorial/maskpointer.md)
* [ONNX Tutorial](tutorial/ONNX.md)
* [ComfyUI-extension-tutorials/ComfyUI-Impact-Pack](https://github.com/ltdrdata/ComfyUI-extension-tutorials/tree/Main/ComfyUI-Impact-Pack)
* [Advanced Tutorial](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/advanced.md)
* [SAM Application](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/sam.md)
* [Mask Painter](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/maskpainter.md)
* [Mask Pointer](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/maskpointer.md)
* [ONNX Tutorial](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/ONNX.md)
* [CLIPSeg Tutorial](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/clipseg.md)
# Credits
@@ -136,4 +149,6 @@ hysts/[anime-face-detector](https://github.com/hysts/anime-face-detector) - Crea
open-mmlab/[mmdetection](https://github.com/open-mmlab/mmdetection) - Object detection toolset. `dd-person_mask2former` was trained via transfer learning using their [R-50 Mask2Former instance segmentation model](https://github.com/open-mmlab/mmdetection/tree/master/configs/mask2former#instance-segmentation) as a base.
biegert/[ComfyUI-CLIPSeg](https://github.com/biegert/ComfyUI-CLIPSeg) - This is a custom node that enables the use of CLIPSeg technology, which can find segments through prompts, in ComfyUI.
WASasquatch/[was-node-suite-comfyui](https://github.com/WASasquatch/was-node-suite-comfyui) - A powerful custom node extensions of ComfyUI.
+144 -9
View File
@@ -1,21 +1,156 @@
import shutil
import folder_paths
import os, sys, subprocess
# ensure .js
print("### Loading: ComfyUI-Impact-Pack")
import os
import sys
comfy_path = os.path.dirname(folder_paths.__file__)
impact_path = os.path.dirname(__file__)
sys.path.append(impact_path)
import impact_config
print(f"### Loading: ComfyUI-Impact-Pack ({impact_config.version})")
# ensure dependency
if impact_config.read_config()[1] < impact_config.dependency_version:
import install # to install dependencies
# Core
# recheck dependencies for colab
try:
import folder_paths
import torch
import cv2
import mmcv
import numpy as np
from mmdet.apis import (inference_detector, init_detector)
import comfy.samplers
import comfy.sd
import warnings
from PIL import Image, ImageFilter
from mmdet.evaluation import get_classes
from skimage.measure import label, regionprops
from collections import namedtuple
except:
print("### ComfyUI-Impact-Pack: Reinstall dependencies (several dependencies are missing.)")
import install
import impact_server # to load server api
def setup_js():
impact_path = os.path.dirname(__file__)
js_dest_path = os.path.join(comfy_path, "web", "extensions", "core")
# remove garbage
old_js_path = os.path.join(comfy_path, "web", "extensions", "core", "impact-pack.js")
if os.path.exists(old_js_path):
os.remove(old_js_path)
# setup js
js_dest_path = os.path.join(comfy_path, "web", "extensions", "impact-pack")
if not os.path.exists(js_dest_path):
os.makedirs(js_dest_path)
js_src_path = os.path.join(impact_path, "js", "impact-pack.js")
shutil.copy(js_src_path, js_dest_path)
setup_js()
from .impact_pack import NODE_CLASS_MAPPINGS
import legacy_nodes
from impact_pack import *
from detectors import *
from impact_pipe import *
__all__ = ['NODE_CLASS_MAPPINGS']
NODE_CLASS_MAPPINGS = {
"SAMLoader": SAMLoader,
"MMDetDetectorProvider": MMDetDetectorProvider,
"CLIPSegDetectorProvider": CLIPSegDetectorProvider,
"ONNXDetectorProvider": ONNXDetectorProvider,
"BitwiseAndMaskForEach": BitwiseAndMaskForEach,
"SubtractMaskForEach": SubtractMaskForEach,
"DetailerForEach": DetailerForEach,
"DetailerForEachDebug": DetailerForEachTest,
"DetailerForEachPipe": DetailerForEachPipe,
"DetailerForEachDebugPipe": DetailerForEachTestPipe,
"SAMDetectorCombined": SAMDetectorCombined,
"FaceDetailer": FaceDetailer,
"FaceDetailerPipe": FaceDetailerPipe,
"ToDetailerPipe": ToDetailerPipe ,
"FromDetailerPipe": FromDetailerPipe,
"ToBasicPipe": ToBasicPipe,
"FromBasicPipe": FromBasicPipe,
"BasicPipeToDetailerPipe": BasicPipeToDetailerPipe,
"DetailerPipeToBasicPipe": DetailerPipeToBasicPipe,
"EditBasicPipe": EditBasicPipe,
"EditDetailerPipe": EditDetailerPipe,
"LatentPixelScale": LatentPixelScale,
"PixelKSampleUpscalerProvider": PixelKSampleUpscalerProvider,
"PixelKSampleUpscalerProviderPipe": PixelKSampleUpscalerProviderPipe,
"IterativeLatentUpscale": IterativeLatentUpscale,
"IterativeImageUpscale": IterativeImageUpscale,
"BitwiseAndMask": BitwiseAndMask,
"SubtractMask": SubtractMask,
"Segs & Mask": SegsBitwiseAndMask,
"EmptySegs": EmptySEGS,
"MaskToSEGS": MaskToSEGS,
"ToBinaryMask": ToBinaryMask,
"MaskPainter": MaskPainter,
"BboxDetectorSEGS": BboxDetectorForEach,
"SegmDetectorSEGS": SegmDetectorForEach,
"ONNXDetectorSEGS": ONNXDetectorForEach,
"BboxDetectorCombined": BboxDetectorCombined,
"SegmDetectorCombined": SegmDetectorCombined,
"SegsToCombinedMask": SegsToCombinedMask,
"MMDetLoader": legacy_nodes.MMDetLoader,
"SegsMaskCombine": legacy_nodes.SegsMaskCombine,
"BboxDetectorForEach": legacy_nodes.BboxDetectorForEach,
"SegmDetectorForEach": legacy_nodes.SegmDetectorForEach,
"BboxDetectorCombined": legacy_nodes.BboxDetectorCombined,
"SegmDetectorCombined": legacy_nodes.SegmDetectorCombined,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"BboxDetectorSEGS": "BBOX Detector (SEGS)",
"SegmDetectorSEGS": "SEGM Detector (SEGS)",
"ONNXDetectorSEGS": "ONNX Detector (SEGS)",
"BboxDetectorCombined": "BBOX Detector (combined)",
"SegmDetectorCombined": "SEGM Detector (combined)",
"SegsToCombinedMask": "SEGS to MASK (combined)",
"MaskToSEGS": "MASK to SEGS",
"BitwiseAndMaskForEach": "Bitwise(SEGS & SEGS)",
"SubtractMaskForEach": "Bitwise(SEGS - SEGS)",
"Segs & Mask": "Bitwise(SEGS & MASK)",
"BitwiseAndMask": "Bitwise(MASK & MASK)",
"SubtractMask": "Bitwise(MASK - MASK)",
"DetailerForEach": "Detailer (SEGS)",
"DetailerForEachPipe": "Detailer (SEGS/pipe)",
"DetailerForEachDebug": "DetailerDebug (SEGS)",
"DetailerForEachDebugPipe": "DetailerDebug (SEGS/pipe)",
"SAMDetectorCombined": "SAMDetector (combined)",
"FaceDetailerPipe": "FaceDetailer (pipe)",
"BasicPipeToDetailerPipe": "BasicPipe -> DetailerPipe",
"DetailerPipeToBasicPipe": "DetailerPipe -> BasicPipe",
"EditBasicPipe": "Edit BasicPipe",
"EditDetailerPipe": "Edit DetailerPipe",
"LatentPixelScale": "Latent Scale (on Pixel Space)",
"IterativeLatentUpscale": "Iterative Upscale (Latent)",
"IterativeImageUpscale": "Iterative Upscale (Image)",
"MMDetLoader": "MMDetLoader (Legacy)",
"SegsMaskCombine": "SegsMaskCombine (Legacy)",
"BboxDetectorForEach": "BboxDetectorForEach (Legacy)",
"SegmDetectorForEach": "SegmDetectorForEach (Legacy)",
"BboxDetectorCombined": "BboxDetectorCombined (Legacy)",
"SegmDetectorCombined": "SegmDetectorCombined (Legacy)",
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+109
View File
@@ -0,0 +1,109 @@
import impact_core as core
class SAMDetectorCombined:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"sam_model": ("SAM_MODEL", ),
"segs": ("SEGS", ),
"image": ("IMAGE", ),
"detection_hint": (["center-1", "horizontal-2", "vertical-2", "rect-4", "diamond-4", "mask-area",
"mask-points", "mask-point-bbox", "none"],),
"dilation": ("INT", {"default": 0, "min": 0, "max": 255, "step": 1}),
"threshold": ("FLOAT", {"default": 0.93, "min": 0.0, "max": 1.0, "step": 0.01}),
"bbox_expansion": ("INT", {"default": 0, "min": 0, "max": 1000, "step": 1}),
"mask_hint_threshold": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}),
"mask_hint_use_negative": (["False", "Small", "Outter"], )
}
}
RETURN_TYPES = ("MASK",)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Detector"
def doit(self, sam_model, segs, image, detection_hint, dilation,
threshold, bbox_expansion, mask_hint_threshold, mask_hint_use_negative):
return (core.make_sam_mask(sam_model, segs, image, detection_hint, dilation,
threshold, bbox_expansion, mask_hint_threshold, mask_hint_use_negative), )
class BboxDetectorForEach:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"bbox_detector": ("BBOX_DETECTOR", ),
"image": ("IMAGE", ),
"threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"dilation": ("INT", {"default": 10, "min": 0, "max": 255, "step": 1}),
"crop_factor": ("FLOAT", {"default": 3.0, "min": 1.0, "max": 10, "step": 0.1}),
}
}
RETURN_TYPES = ("SEGS", )
FUNCTION = "doit"
CATEGORY = "ImpactPack/Detector"
def doit(self, bbox_detector, image, threshold, dilation, crop_factor):
segs = bbox_detector.detect(image, threshold, dilation, crop_factor)
return (segs, )
class SegmDetectorForEach:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"segm_detector": ("SEGM_DETECTOR", ),
"image": ("IMAGE", ),
"threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"dilation": ("INT", {"default": 10, "min": 0, "max": 255, "step": 1}),
"crop_factor": ("FLOAT", {"default": 3.0, "min": 1.0, "max": 10, "step": 0.1}),
}
}
RETURN_TYPES = ("SEGS", )
FUNCTION = "doit"
CATEGORY = "ImpactPack/Detector"
def doit(self, segm_detector, image, threshold, dilation, crop_factor):
segs = segm_detector.detect(image, threshold, dilation, crop_factor)
return (segs, )
class SegmDetectorCombined:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"segm_detector": ("SEGM_DETECTOR", ),
"image": ("IMAGE", ),
"threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"dilation": ("INT", {"default": 0, "min": 0, "max": 255, "step": 1}),
}
}
RETURN_TYPES = ("MASK",)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Detector"
def doit(self, segm_detector, image, threshold, dilation):
mask = segm_detector.detect_combined(image, threshold, dilation)
return (mask,)
class BboxDetectorCombined(SegmDetectorCombined):
@classmethod
def INPUT_TYPES(s):
return {"required": {
"bbox_detector": ("BBOX_DETECTOR", ),
"image": ("IMAGE", ),
"threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"dilation": ("INT", {"default": 4, "min": 0, "max": 255, "step": 1}),
}
}
def doit(self, bbox_detector, image, threshold, dilation):
mask = bbox_detector.detect_combined(image, threshold, dilation)
return (mask,)
+2
View File
@@ -1,6 +1,8 @@
import configparser
import os
version = "V2.0"
dependency_version = 1
my_path = os.path.dirname(__file__)
+748
View File
@@ -0,0 +1,748 @@
import os
import sys
import mmcv
from mmdet.apis import (inference_detector, init_detector)
from mmdet.evaluation import get_classes
from segment_anything import SamPredictor
import cv2
from impact_utils import *
from collections import namedtuple
import numpy as np
from skimage.measure import label, regionprops
main_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
sys.path.append(os.path.dirname(__file__))
sys.path.append(main_dir)
import nodes
import onnx
import comfy_extras.nodes_upscale_model as model_upscale
SEG = namedtuple("SEG", ['cropped_image', 'cropped_mask', 'confidence', 'crop_region', 'bbox', 'label'],
defaults=[None])
class NO_BBOX_DETECTOR:
pass
class NO_SEGM_DETECTOR:
pass
def load_mmdet(model_path):
model_config = os.path.splitext(model_path)[0] + ".py"
model = init_detector(model_config, model_path, device="cpu")
return model
def create_segmasks(results):
bboxs = results[1]
segms = results[2]
confidence = results[3]
results = []
for i in range(len(segms)):
item = (bboxs[i], segms[i].astype(np.float32), confidence[i])
results.append(item)
return results
def inference_segm_old(model, image, conf_threshold):
image = image.numpy()[0] * 255
mmdet_results = inference_detector(model, image)
bbox_results, segm_results = mmdet_results
label = "A"
classes = get_classes("coco")
labels = [
np.full(bbox.shape[0], i, dtype=np.int32)
for i, bbox in enumerate(bbox_results)
]
n, m = bbox_results[0].shape
if n == 0:
return [[], [], []]
labels = np.concatenate(labels)
bboxes = np.vstack(bbox_results)
segms = mmcv.concat_list(segm_results)
filter_idxs = np.where(bboxes[:, -1] > conf_threshold)[0]
results = [[], [], []]
for i in filter_idxs:
results[0].append(label + "-" + classes[labels[i]])
results[1].append(bboxes[i])
results[2].append(segms[i])
return results
def inference_segm(image, modelname, conf_thres, lab="A"):
image = image.numpy()[0] * 255
mmdet_results = inference_detector(modelname, image).pred_instances
bboxes = mmdet_results.bboxes.numpy()
segms = mmdet_results.masks.numpy()
scores = mmdet_results.scores.numpy()
classes = get_classes("coco")
n, m = bboxes.shape
if n == 0:
return [[], [], [], []]
labels = mmdet_results.labels
filter_inds = np.where(mmdet_results.scores > conf_thres)[0]
results = [[], [], [], []]
for i in filter_inds:
results[0].append(lab + "-" + classes[labels[i]])
results[1].append(bboxes[i])
results[2].append(segms[i])
results[3].append(scores[i])
return results
def inference_bbox(modelname, image, conf_threshold):
image = image.numpy()[0] * 255
label = "A"
output = inference_detector(modelname, image).pred_instances
cv2_image = np.array(image)
cv2_image = cv2_image[:, :, ::-1].copy()
cv2_gray = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2GRAY)
segms = []
for x0, y0, x1, y1 in output.bboxes:
cv2_mask = np.zeros(cv2_gray.shape, np.uint8)
cv2.rectangle(cv2_mask, (int(x0), int(y0)), (int(x1), int(y1)), 255, -1)
cv2_mask_bool = cv2_mask.astype(bool)
segms.append(cv2_mask_bool)
n, m = output.bboxes.shape
if n == 0:
return [[], [], [], []]
bboxes = output.bboxes.numpy()
scores = output.scores.numpy()
filter_idxs = np.where(scores > conf_threshold)[0]
results = [[], [], [], []]
for i in filter_idxs:
results[0].append(label)
results[1].append(bboxes[i])
results[2].append(segms[i])
results[3].append(scores[i])
return results
def gen_detection_hints_from_mask_area(x, y, mask, threshold, use_negative):
points = []
plabs = []
# minimum sampling step >= 3
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))
plabs.append(1)
elif use_negative and mask[i][j] == 0:
points.append((x+j, y+i))
plabs.append(0)
return points, plabs
def gen_negative_hints(w, h, x1, y1, x2, y2):
npoints = []
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):
npoints.append((j, i))
nplabs.append(0)
return npoints, nplabs
def enhance_detail(image, model, vae, guide_size, guide_size_for, bbox, seed, steps, cfg, sampler_name, scheduler,
positive, negative, denoise, noise_mask, force_inpaint):
h = image.shape[1]
w = image.shape[2]
bbox_h = bbox[3] - bbox[1]
bbox_w = bbox[2] - bbox[0]
# Skip processing if the detected bbox is already larger than the guide_size
if bbox_h >= guide_size and bbox_w >= guide_size:
print(f"Detailer: segment skip")
None
if guide_size_for == "bbox":
# Scale up based on the smaller dimension between width and height.
upscale = guide_size / min(bbox_w, bbox_h)
else:
# for cropped_size
upscale = guide_size / min(w, h)
new_w = int(((w * upscale) // 8) * 8)
new_h = int(((h * upscale) // 8) * 8)
if not force_inpaint:
if upscale <= 1.0:
print(f"Detailer: segment skip [determined upscale factor={upscale}]")
return None
if new_w == 0 or new_h == 0:
print(f"Detailer: segment skip [zero size={new_w, new_h}]")
return None
else:
if upscale <= 1.0 or new_w == 0 or new_h == 0:
print(f"Detailer: force inpaint")
upscale = 1.0
new_w = w
new_h = h
print(f"Detailer: segment upscale for ({bbox_w, bbox_h}) | crop region {w, h} x {upscale} -> {new_w, new_h}")
# upscale
upscaled_image = scale_tensor(new_w, new_h, torch.from_numpy(image))
# ksampler
latent_image = to_latent_image(upscaled_image, vae)
if noise_mask is not None:
# upscale the mask tensor by a factor of 2 using bilinear interpolation
noise_mask = torch.from_numpy(noise_mask)
upscaled_mask = torch.nn.functional.interpolate(noise_mask.unsqueeze(0).unsqueeze(0), size=(new_h, new_w),
mode='bilinear', align_corners=False)
# remove the extra dimensions added by unsqueeze
upscaled_mask = upscaled_mask.squeeze().squeeze()
latent_image['noise_mask'] = upscaled_mask
sampler = nodes.KSampler()
refined_latent = sampler.sample(model, seed, steps, cfg, sampler_name, scheduler,
positive, negative, latent_image, denoise)
refined_latent = refined_latent[0]
# non-latent downscale - latent downscale cause bad quality
refined_image = vae.decode(refined_latent['samples'])
# downscale
refined_image = scale_tensor_and_to_pil(w, h, refined_image)
# don't convert to latent - latent break image
# preserving pil is much better
return refined_image
def composite_to(dest_latent, crop_region, src_latent):
x1 = crop_region[0]
y1 = crop_region[1]
# composite to original latent
lc = nodes.LatentComposite()
# 현재 mask 를 고려한 composite 가 없음... 이거 처리 필요.
orig_image = lc.composite(dest_latent, src_latent, x1, y1)
return orig_image[0]
def sam_predict(predictor, points, plabs, bbox, threshold):
point_coords = None if not points else np.array(points)
point_labels = None if not plabs else np.array(plabs)
box = np.array([bbox]) if bbox is not None else None
cur_masks, scores, _ = predictor.predict(point_coords=point_coords, point_labels=point_labels, box=box)
total_masks = []
selected = False
max_score = 0
for idx in range(len(scores)):
if scores[idx] > max_score:
max_score = scores[idx]
max_mask = cur_masks[idx]
if scores[idx] >= threshold:
selected = True
total_masks.append(cur_masks[idx])
else:
pass
if not selected:
total_masks.append(max_mask)
return total_masks
def make_sam_mask(sam_model, segs, image, detection_hint, dilation,
threshold, bbox_expansion, mask_hint_threshold, mask_hint_use_negative):
predictor = SamPredictor(sam_model)
image = np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)
predictor.set_image(image, "RGB")
total_masks = []
use_small_negative = mask_hint_use_negative == "Small"
# seg_shape = segs[0]
segs = segs[1]
if detection_hint == "mask-points":
points = []
plabs = []
for i in range(len(segs)):
bbox = segs[i].bbox
center = center_of_bbox(segs[i].bbox)
points.append(center)
# small point is background, big point is foreground
if use_small_negative and bbox[2] - bbox[0] < 10:
plabs.append(0)
else:
plabs.append(1)
detected_masks = sam_predict(predictor, points, plabs, None, threshold)
total_masks += detected_masks
else:
for i in range(len(segs)):
bbox = segs[i].bbox
center = center_of_bbox(bbox)
x1 = max(bbox[0] - bbox_expansion, 0)
y1 = max(bbox[1] - bbox_expansion, 0)
x2 = min(bbox[2] + bbox_expansion, image.shape[1])
y2 = min(bbox[3] + bbox_expansion, image.shape[0])
dilated_bbox = [x1, y1, x2, y2]
points = []
plabs = []
if detection_hint == "center-1":
points.append(center)
plabs = [1] # 1 = foreground point, 0 = background point
elif detection_hint == "horizontal-2":
gap = (x2 - x1) / 3
points.append((x1 + gap, center[1]))
points.append((x1 + gap * 2, center[1]))
plabs = [1, 1]
elif detection_hint == "vertical-2":
gap = (y2 - y1) / 3
points.append((center[0], y1 + gap))
points.append((center[0], y1 + gap * 2))
plabs = [1, 1]
elif detection_hint == "rect-4":
x_gap = (x2 - x1) / 3
y_gap = (y2 - y1) / 3
points.append((x1 + x_gap, center[1]))
points.append((x1 + x_gap * 2, center[1]))
points.append((center[0], y1 + y_gap))
points.append((center[0], y1 + y_gap * 2))
plabs = [1, 1, 1, 1]
elif detection_hint == "diamond-4":
x_gap = (x2 - x1) / 3
y_gap = (y2 - y1) / 3
points.append((x1 + x_gap, y1 + y_gap))
points.append((x1 + x_gap * 2, y1 + y_gap))
points.append((x1 + x_gap, y1 + y_gap * 2))
points.append((x1 + x_gap * 2, y1 + y_gap * 2))
plabs = [1, 1, 1, 1]
elif detection_hint == "mask-point-bbox":
center = center_of_bbox(segs[i].bbox)
points.append(center)
plabs = [1]
elif detection_hint == "mask-area":
points, plabs = gen_detection_hints_from_mask_area(segs[i].crop_region[0], segs[i].crop_region[1],
segs[i].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],
segs[i].crop_region[0], segs[i].crop_region[1],
segs[i].crop_region[2], segs[i].crop_region[3])
points += npoints
plabs += nplabs
detected_masks = sam_predict(predictor, points, plabs, dilated_bbox, threshold)
total_masks += detected_masks
# merge every collected masks
mask = combine_masks2(total_masks)
if mask is not None:
mask = mask.float()
mask = dilate_mask(mask.numpy(), dilation)
mask = torch.from_numpy(mask)
else:
mask = torch.zeros((8, 8), dtype=torch.float32, device="cpu") # empty mask
return mask
def segs_bitwise_and_mask(segs, mask):
if mask is None:
print("[SegsBitwiseAndMask] Cannot operate: MASK is empty.")
return ([], )
items = []
mask = (mask.numpy() * 255).astype(np.uint8)
for seg in segs[1]:
cropped_mask = (seg.cropped_mask * 255).astype(np.uint8)
crop_region = seg.crop_region
cropped_mask2 = mask[crop_region[1]:crop_region[3], crop_region[0]:crop_region[2]]
new_mask = np.bitwise_and(cropped_mask.astype(np.uint8), cropped_mask2)
new_mask = new_mask.astype(np.float32) / 255.0
item = SEG(seg.cropped_image, new_mask, seg.confidence, seg.crop_region, seg.bbox, seg.label)
items.append(item)
return segs[0], items
class BBoxDetector:
bbox_model = None
def __init__(self, bbox_model):
self.bbox_model = bbox_model
def detect(self, image, threshold, dilation, crop_factor):
mmdet_results = inference_bbox(self.bbox_model, image, threshold)
segmasks = create_segmasks(mmdet_results)
if dilation > 0:
segmasks = dilate_masks(segmasks, dilation)
items = []
h = image.shape[1]
w = image.shape[2]
for x in segmasks:
item_bbox = x[0]
item_mask = x[1]
crop_region = make_crop_region(w, h, item_bbox, crop_factor)
cropped_image = crop_image(image, crop_region)
cropped_mask = crop_ndarray2(item_mask, crop_region)
confidence = x[2]
# bbox_size = (item_bbox[2]-item_bbox[0],item_bbox[3]-item_bbox[1]) # (w,h)
item = SEG(cropped_image, cropped_mask, confidence, crop_region, item_bbox)
items.append(item)
shape = image.shape[1],image.shape[2]
return shape, items
def detect_combined(self, image, threshold, dilation):
mmdet_results = inference_bbox(self.bbox_model, image, threshold)
segmasks = create_segmasks(mmdet_results)
if dilation > 0:
segmasks = dilate_masks(segmasks, dilation)
return combine_masks(segmasks)
def setAux(self, x):
pass
class ONNXDetector(BBoxDetector):
onnx_model = None
def __init__(self, onnx_model):
self.onnx_model = onnx_model
def detect(self, image, threshold, dilation, crop_factor):
h = image.shape[1]
w = image.shape[2]
labels, scores, boxes = onnx.onnx_inference(image, self.onnx_model)
# collect feasible item
result = []
for i in range(len(labels)):
if scores[i] > threshold:
item_bbox = boxes[i]
x1, y1, x2, y2 = item_bbox
crop_region = make_crop_region(w, h, item_bbox, crop_factor)
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
# make items
item = SEG(None, cropped_mask, scores[i], crop_region, item_bbox)
result.append(item)
shape = h, w
return shape, result
def detect_combined(self, image, threshold, dilation):
return segs_to_combined_mask(self.detect(image, threshold, dilation, 1))
def setAux(self, x):
pass
class SegmDetector(BBoxDetector):
segm_model = None
def __init__(self, segm_model):
self.segm_model = segm_model
def detect(self, image, threshold, dilation, crop_factor):
mmdet_results = inference_segm(image, self.segm_model, threshold)
segmasks = create_segmasks(mmdet_results)
if dilation > 0:
segmasks = dilate_masks(segmasks, dilation)
items = []
h = image.shape[1]
w = image.shape[2]
for x in segmasks:
item_bbox = x[0]
item_mask = x[1]
crop_region = make_crop_region(w, h, item_bbox, crop_factor)
cropped_image = crop_image(image, crop_region)
cropped_mask = crop_ndarray2(item_mask, crop_region)
confidence = x[2]
item = SEG(cropped_image, cropped_mask, confidence, crop_region, item_bbox)
items.append(item)
return image.shape, items
def detect_combined(self, image, threshold, dilation):
mmdet_results = inference_bbox(self.bbox_model, image, threshold)
segmasks = create_segmasks(mmdet_results)
if dilation > 0:
segmasks = dilate_masks(segmasks, dilation)
return combine_masks(segmasks)
def setAux(self, x):
pass
def mask_to_segs(mask, combined, crop_factor, bbox_fill):
if mask is None:
print("[mask_to_segs] Cannot operate: MASK is empty.")
return ([], )
mask = mask.numpy()
result = []
if combined == "True":
# Find the indices of the non-zero elements
indices = np.nonzero(mask)
if len(indices[0]) > 0 and len(indices[1]) > 0:
# Determine the bounding box of the non-zero elements
bbox = np.min(indices[1]), np.min(indices[0]), np.max(indices[1]), np.max(indices[0])
crop_region = make_crop_region(mask.shape[1], mask.shape[0], bbox, crop_factor)
x1, y1, x2, y2 = crop_region
if x2 - x1 > 0 and y2 - y1 > 0:
cropped_mask = mask[y1:y2, x1:x2]
item = SEG(None, cropped_mask, 1.0, crop_region, bbox, 'A')
result.append(item)
else:
# label the connected components
labelled_mask = label(mask)
# get the region properties for each connected component
regions = regionprops(labelled_mask)
# iterate over the regions and print their bounding boxes
for region in regions:
y1, x1, y2, x2 = region.bbox
bbox = x1, y1, x2, y2
crop_region = make_crop_region(mask.shape[1], mask.shape[0], bbox, crop_factor)
if x2 - x1 > 0 and y2 - y1 > 0:
cropped_mask = np.array(mask[crop_region[1]:crop_region[3], crop_region[0]:crop_region[2]])
if bbox_fill:
cropped_mask.fill(1.0)
item = SEG(None, cropped_mask, 1.0, crop_region, bbox, 'A')
result.append(item)
if not result:
print(f"[mask_to_segs] Empty mask.")
print(f"# of Detected SEGS: {len(result)}")
# for r in result:
# print(f"\tbbox={r.bbox}, crop={r.crop_region}, label={r.label}")
return mask.shape, result
def segs_to_combined_mask(segs):
shape = segs[0]
h = shape[0]
w = shape[1]
mask = np.zeros((h, w), dtype=np.uint8)
for seg in segs[1]:
cropped_mask = seg.cropped_mask
crop_region = seg.crop_region
mask[crop_region[1]:crop_region[3], crop_region[0]:crop_region[2]] |= (cropped_mask * 255).astype(np.uint8)
return torch.from_numpy(mask.astype(np.float32) / 255.0)
def latent_upscale_on_pixel_space_shape(samples, scale_method, w, h, vae):
pixels = nodes.VAEDecode().decode(vae, samples)[0]
new_w = max(8, (w//8)*8)
new_h = max(8, (h//8)*8)
pixels = nodes.ImageScale().upscale(pixels, scale_method, int(new_w), int(new_h), False)
return nodes.VAEEncode().encode(vae, pixels[0])[0]
def latent_upscale_on_pixel_space(samples, scale_method, scale_factor, vae):
pixels = nodes.VAEDecode().decode(vae, samples)[0]
w = pixels.shape[2] * scale_factor
h = pixels.shape[1] * scale_factor
pixels = nodes.ImageScale().upscale(pixels, scale_method, int(w), int(h), False)
return nodes.VAEEncode().encode(vae, pixels[0])[0]
def latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, upscale_model, new_w, new_h, vae):
pixels = nodes.VAEDecode().decode(vae, samples)[0]
w = pixels.shape[2]
# upscale by model upscaler
current_w = w
while current_w < new_w:
pixels = model_upscale.ImageUpscaleWithModel().upscale(upscale_model, pixels)[0]
current_w = pixels.shape[2]
# downscale to target scale
pixels = nodes.ImageScale().upscale(pixels, scale_method, int(new_w), int(new_h), False)
return nodes.VAEEncode().encode(vae, pixels[0])[0]
def latent_upscale_on_pixel_space_with_model(samples, scale_method, upscale_model, scale_factor, vae):
pixels = nodes.VAEDecode().decode(vae, samples)[0]
w = pixels.shape[2]
h = pixels.shape[1]
new_w = w * scale_factor
new_h = h * scale_factor
# upscale by model upscaler
current_w = w
while current_w < new_w:
pixels = model_upscale.ImageUpscaleWithModel().upscale(upscale_model, pixels)[0]
current_w = pixels.shape[2]
# downscale to target scale
pixels = nodes.ImageScale().upscale(pixels, scale_method, int(new_w), int(new_h), False)
return nodes.VAEEncode().encode(vae, pixels[0])[0]
class PixelKSampleUpscaler:
params = None
upscale_model = None
def __init__(self, scale_method, model, vae, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise, upscale_model_opt=None):
self.params = scale_method, model, vae, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise
self.upscale_model = upscale_model_opt
def upscale(self, samples, upscale_factor):
scale_method, model, vae, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise = self.params
if self.upscale_model is None:
upscaled_latent = latent_upscale_on_pixel_space(samples, scale_method, upscale_factor, vae)
else:
upscaled_latent = latent_upscale_on_pixel_space_with_model(samples, scale_method, self.upscale_model, upscale_factor, vae)
refined_latent = nodes.KSampler().sample(model, seed, steps, cfg, sampler_name, scheduler,
positive, negative, upscaled_latent, denoise)
return refined_latent
def upscale_shape(self, samples, w, h):
scale_method, model, vae, seed, steps, cfg, sampler_name, scheduler, positive, negative, denoise = self.params
if self.upscale_model is None:
upscaled_latent = latent_upscale_on_pixel_space_shape(samples, scale_method, w, h, vae)
else:
upscaled_latent = latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, self.upscale_model, w, h, vae)
refined_latent = nodes.KSampler().sample(model, seed, steps, cfg, sampler_name, scheduler,
positive, negative, upscaled_latent, denoise)
return refined_latent[0]
try:
class BBoxDetectorBasedOnCLIPSeg(BBoxDetector):
prompt = None
blur = None
threshold = None
dilation_factor = None
aux = None
def __init__(self, prompt, blur, threshold, dilation_factor):
self.prompt = prompt
self.blur = blur
self.threshold = threshold
self.dilation_factor = dilation_factor
def detect(self, image, bbox_threshold, bbox_dilation, bbox_crop_factor):
mask = self.detect_combined(image, bbox_threshold, bbox_dilation)
segs = mask_to_segs(mask, False, bbox_crop_factor, True)
return segs
def detect_combined(self, image, bbox_threshold, bbox_dilation):
from custom_nodes.clipseg import CLIPSeg
if self.threshold is None:
threshold = bbox_threshold
else:
threshold = self.threshold
if self.dilation_factor is None:
dilation_factor = bbox_dilation
else:
dilation_factor = self.dilation_factor
prompt = self.aux if self.prompt == '' and self.aux is not None else self.prompt
mask, _, _ = CLIPSeg().segment_image(image, prompt, self.blur, threshold, dilation_factor)
mask = to_binary_mask(mask)
return mask
def setAux(self, x):
self.aux = x
except:
pass
+388 -1033
View File
File diff suppressed because it is too large Load Diff
+205
View File
@@ -0,0 +1,205 @@
class ToDetailerPipe:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"model": ("MODEL",),
"vae": ("VAE",),
"positive": ("CONDITIONING",),
"negative": ("CONDITIONING",),
"bbox_detector": ("BBOX_DETECTOR", ),
},
"optional": {
"sam_model_opt": ("SAM_MODEL", ),
}}
RETURN_TYPES = ("DETAILER_PIPE", )
RETURN_NAMES = ("detailer_pipe", )
FUNCTION = "doit"
CATEGORY = "ImpactPack/Pipe"
def doit(self, model, vae, positive, negative, bbox_detector, sam_model_opt=None):
pipe = (model, vae, positive, negative, bbox_detector, sam_model_opt)
return (pipe, )
class FromDetailerPipe:
@classmethod
def INPUT_TYPES(s):
return {"required": {"detailer_pipe": ("DETAILER_PIPE",), }, }
RETURN_TYPES = ("MODEL", "VAE", "CONDITIONING", "CONDITIONING", "BBOX_DETECTOR", "SAM_MODEL")
RETURN_NAMES = ("model", "vae", "positive", "negative", "bbox_detector", "sam_model_opt")
FUNCTION = "doit"
CATEGORY = "ImpactPack/Pipe"
def doit(self, detailer_pipe):
model, vae, positive, negative, bbox_detector, sam_model_opt = detailer_pipe
return model, vae, positive, negative, bbox_detector, sam_model_opt
class ToBasicPipe:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"model": ("MODEL",),
"clip": ("CLIP",),
"vae": ("VAE",),
"positive": ("CONDITIONING",),
"negative": ("CONDITIONING",),
},
}
RETURN_TYPES = ("BASIC_PIPE", )
RETURN_NAMES = ("basic_pipe", )
FUNCTION = "doit"
CATEGORY = "ImpactPack/Pipe"
def doit(self, model, clip, vae, positive, negative):
pipe = (model, clip, vae, positive, negative)
return (pipe, )
class FromBasicPipe:
@classmethod
def INPUT_TYPES(s):
return {"required": { "basic_pipe": ("BASIC_PIPE",), }, }
RETURN_TYPES = ("MODEL", "CLIP", "VAE", "CONDITIONING", "CONDITIONING")
RETURN_NAMES = ("model", "clip", "vae", "positive", "negative")
FUNCTION = "doit"
CATEGORY = "ImpactPack/Pipe"
def doit(self, basic_pipe):
model, clip, vae, positive, negative = basic_pipe
return model, clip, vae, positive, negative
class BasicPipeToDetailerPipe:
@classmethod
def INPUT_TYPES(s):
return {"required": {"basic_pipe": ("BASIC_PIPE",),
"bbox_detector": ("BBOX_DETECTOR", ), },
"optional": {"sam_model_opt": ("SAM_MODEL", ), },
}
RETURN_TYPES = ("DETAILER_PIPE", )
RETURN_NAMES = ("detailer_pipe", )
FUNCTION = "doit"
CATEGORY = "ImpactPack/Pipe"
def doit(self, basic_pipe, bbox_detector, sam_model_opt=None):
model, _, vae, positive, negative = basic_pipe
pipe = model, vae, positive, negative, bbox_detector, sam_model_opt
return (pipe, )
class DetailerPipeToBasicPipe:
@classmethod
def INPUT_TYPES(s):
return {"required": {"detailer_pipe": ("DETAILER_PIPE",),
"clip": ("CLIP",), }, }
RETURN_TYPES = ("BASIC_PIPE", )
RETURN_NAMES = ("basic_pipe", )
FUNCTION = "doit"
CATEGORY = "ImpactPack/Pipe"
def doit(self, detailer_pipe, clip):
model, vae, positive, negative, _, _ = detailer_pipe
pipe = model, clip, vae, positive, negative
return (pipe, )
class EditBasicPipe:
@classmethod
def INPUT_TYPES(s):
return {
"required": {"basic_pipe": ("BASIC_PIPE",), },
"optional": {
"model": ("MODEL",),
"clip": ("CLIP",),
"vae": ("VAE",),
"positive": ("CONDITIONING",),
"negative": ("CONDITIONING",),
},
}
RETURN_TYPES = ("BASIC_PIPE", )
RETURN_NAMES = ("basic_pipe", )
FUNCTION = "doit"
CATEGORY = "ImpactPack/Pipe"
def doit(self, basic_pipe, model=None, clip=None, vae=None, positive=None, negative=None):
res_model, res_clip, res_vae, res_positive, res_negative = basic_pipe
if model is not None:
res_model = model
if clip is not None:
res_clip = clip
if vae is not None:
res_vae = vae
if positive is not None:
res_positive = positive
if negative is not None:
res_negative = negative
pipe = res_model, res_clip, res_vae, res_positive, res_negative
return (pipe, )
class EditDetailerPipe:
@classmethod
def INPUT_TYPES(s):
return {
"required": {"detailer_pipe": ("DETAILER_PIPE",), },
"optional": {
"model": ("MODEL",),
"vae": ("VAE",),
"positive": ("CONDITIONING",),
"negative": ("CONDITIONING",),
"bbox_detector": ("BBOX_DETECTOR",),
"sam_model": ("SAM_MODEL",), },
}
RETURN_TYPES = ("BASIC_PIPE",)
RETURN_NAMES = ("basic_pipe",)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Pipe"
def doit(self, detailer_pipe, model=None, vae=None, positive=None, negative=None, bbox_detector=None, sam_model=None):
res_model, res_vae, res_positive, res_negative, res_bbox_detector, res_sam_model = detailer_pipe
if model is not None:
res_model = model
if vae is not None:
res_vae = vae
if positive is not None:
res_positive = positive
if negative is not None:
res_negative = negative
if bbox_detector is not None:
res_bbox_detector = bbox_detector
if sam_model is not None:
res_sam_model = sam_model
pipe = res_model, res_vae, res_positive, res_negative, res_bbox_detector, res_sam_model
return (pipe, )
+35
View File
@@ -0,0 +1,35 @@
import os
from aiohttp import web
import server
import folder_paths
@server.PromptServer.instance.routes.post("/upload/temp")
async def upload_image(request):
upload_dir = folder_paths.get_temp_directory()
if not os.path.exists(upload_dir):
os.makedirs(upload_dir)
post = await request.post()
image = post.get("image")
if image and image.file:
filename = image.filename
if not filename:
return web.Response(status=400)
split = os.path.splitext(filename)
i = 1
while os.path.exists(os.path.join(upload_dir, filename)):
filename = f"{split[0]} ({i}){split[1]}"
i += 1
filepath = os.path.join(upload_dir, filename)
with open(filepath, "wb") as f:
f.write(image.file.read())
return web.json_response({"name": filename})
else:
return web.Response(status=400)
+189
View File
@@ -0,0 +1,189 @@
import torch
import cv2
import numpy as np
from PIL import Image, ImageFilter
LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS)
def pil2tensor(image):
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
def tensor2pil(image):
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
def center_of_bbox(bbox):
w, h = bbox[2] - bbox[0], bbox[3] - bbox[1]
return bbox[0] + w/2, bbox[1] + h/2
def combine_masks(masks):
if len(masks) == 0:
return None
else:
initial_cv2_mask = np.array(masks[0][1])
combined_cv2_mask = initial_cv2_mask
for i in range(1, len(masks)):
cv2_mask = np.array(masks[i][1])
combined_cv2_mask = cv2.bitwise_or(combined_cv2_mask, cv2_mask)
mask = torch.from_numpy(combined_cv2_mask)
return mask
def combine_masks2(masks):
if len(masks) == 0:
return None
else:
initial_cv2_mask = np.array(masks[0]).astype(np.uint8)
combined_cv2_mask = initial_cv2_mask
for i in range(1, len(masks)):
cv2_mask = np.array(masks[i]).astype(np.uint8)
combined_cv2_mask = cv2.bitwise_or(combined_cv2_mask, cv2_mask)
mask = torch.from_numpy(combined_cv2_mask)
return mask
def bitwise_and_masks(mask1, mask2):
cv2_mask1 = np.array(mask1)
cv2_mask2 = np.array(mask2)
cv2_mask = cv2.bitwise_and(cv2_mask1, cv2_mask2)
mask = torch.from_numpy(cv2_mask)
return mask
def to_binary_mask(mask):
mask = mask.clone()
mask[mask != 0] = 1.
return mask
def dilate_mask(mask, dilation_factor, iter=1):
if dilation_factor == 0:
return mask
kernel = np.ones((dilation_factor,dilation_factor), np.uint8)
return cv2.dilate(mask, kernel, iter)
def dilate_masks(segmasks, dilation_factor, iter=1):
if dilation_factor == 0:
return segmasks
dilated_masks = []
kernel = np.ones((dilation_factor,dilation_factor), np.uint8)
for i in range(len(segmasks)):
cv2_mask = segmasks[i][1]
dilated_mask = cv2.dilate(cv2_mask, kernel, iter)
item = (segmasks[i][0], dilated_mask, segmasks[i][2])
dilated_masks.append(item)
return dilated_masks
def feather_mask(mask, thickness):
pil_mask = Image.fromarray(np.uint8(mask * 255))
# Create a feathered mask by applying a Gaussian blur to the mask
blurred_mask = pil_mask.filter(ImageFilter.GaussianBlur(thickness))
feathered_mask = Image.new("L", pil_mask.size, 0)
feathered_mask.paste(blurred_mask, (0, 0), blurred_mask)
return feathered_mask
def subtract_masks(mask1, mask2):
cv2_mask1 = np.array(mask1) * 255
cv2_mask2 = np.array(mask2) * 255
cv2_mask = cv2.subtract(cv2_mask1, cv2_mask2)
mask = torch.from_numpy(cv2_mask) / 255.0
return mask
def normalize_region(limit, startp, size):
if startp < 0:
new_endp = min(limit, size)
new_startp = 0
elif startp + size > limit:
new_startp = limit - size
new_endp = limit
else:
new_startp = startp
new_endp = min(limit, startp+size)
return int(new_startp), int(new_endp)
def make_crop_region(w, h, bbox, crop_factor):
x1 = bbox[0]
y1 = bbox[1]
x2 = bbox[2]
y2 = bbox[3]
bbox_w = x2 - x1
bbox_h = y2 - y1
crop_w = bbox_w * crop_factor
crop_h = bbox_h * crop_factor
kernel_x = x1 + bbox_w / 2
kernel_y = y1 + bbox_h / 2
new_x1 = int(kernel_x - crop_w / 2)
new_y1 = int(kernel_y - crop_h / 2)
# make sure position in (w,h)
new_x1, new_x2 = normalize_region(w, new_x1, crop_w)
new_y1, new_y2 = normalize_region(h, new_y1, crop_h)
return [new_x1, new_y1, new_x2, new_y2]
def crop_ndarray4(npimg, crop_region):
x1 = crop_region[0]
y1 = crop_region[1]
x2 = crop_region[2]
y2 = crop_region[3]
cropped = npimg[:, y1:y2, x1:x2, :]
return cropped
def crop_ndarray2(npimg, crop_region):
x1 = crop_region[0]
y1 = crop_region[1]
x2 = crop_region[2]
y2 = crop_region[3]
cropped = npimg[y1:y2, x1:x2]
return cropped
def crop_image(image, crop_region):
return crop_ndarray4(np.array(image), crop_region)
def to_latent_image(pixels, vae):
x = (pixels.shape[1] // 8) * 8
y = (pixels.shape[2] // 8) * 8
if pixels.shape[1] != x or pixels.shape[2] != y:
pixels = pixels[:, :x, :y, :]
t = vae.encode(pixels[:, :, :, :3])
return {"samples": t}
def scale_tensor(w, h, image):
image = tensor2pil(image)
scaled_image = image.resize((w, h), resample=LANCZOS)
return pil2tensor(scaled_image)
def scale_tensor_and_to_pil(w,h, image):
image = tensor2pil(image)
return image.resize((w, h), resample=LANCZOS)
View File
+203
View File
@@ -0,0 +1,203 @@
import folder_paths
import impact_core as core
from impact_utils import *
from impact_core import SEG
class NO_BBOX_MODEL:
pass
class NO_SEGM_MODEL:
pass
class MMDetLoader:
@classmethod
def INPUT_TYPES(s):
bboxs = ["bbox/"+x for x in folder_paths.get_filename_list("mmdets_bbox")]
segms = ["segm/"+x for x in folder_paths.get_filename_list("mmdets_segm")]
return {"required": {"model_name": (bboxs + segms, )}}
RETURN_TYPES = ("BBOX_MODEL", "SEGM_MODEL")
FUNCTION = "load_mmdet"
CATEGORY = "ImpactPack/Legacy"
def load_mmdet(self, model_name):
mmdet_path = folder_paths.get_full_path("mmdets", model_name)
model = core.load_mmdet(mmdet_path)
if model_name.startswith("bbox"):
return model, NO_SEGM_MODEL()
else:
return NO_BBOX_MODEL(), model
class BboxDetectorForEach:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"bbox_model": ("BBOX_MODEL", ),
"image": ("IMAGE", ),
"threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"dilation": ("INT", {"default": 10, "min": 0, "max": 255, "step": 1}),
"crop_factor": ("FLOAT", {"default": 3.0, "min": 1.0, "max": 10, "step": 0.1}),
}
}
RETURN_TYPES = ("SEGS", )
FUNCTION = "doit"
CATEGORY = "ImpactPack/Legacy"
@staticmethod
def detect(bbox_model, image, threshold, dilation, crop_factor):
mmdet_results = core.inference_bbox(bbox_model, image, threshold)
segmasks = core.create_segmasks(mmdet_results)
if dilation > 0:
segmasks = dilate_masks(segmasks, dilation)
items = []
h = image.shape[1]
w = image.shape[2]
for x in segmasks:
item_bbox = x[0]
item_mask = x[1]
crop_region = make_crop_region(w, h, item_bbox, crop_factor)
cropped_image = crop_image(image, crop_region)
cropped_mask = crop_ndarray2(item_mask, crop_region)
confidence = x[2]
# bbox_size = (item_bbox[2]-item_bbox[0],item_bbox[3]-item_bbox[1]) # (w,h)
item = SEG(cropped_image, cropped_mask, confidence, crop_region, item_bbox)
items.append(item)
shape = h, w
return shape, items
def doit(self, bbox_model, image, threshold, dilation, crop_factor):
return (BboxDetectorForEach.detect(bbox_model, image, threshold, dilation, crop_factor), )
class SegmDetectorCombined:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"segm_model": ("SEGM_MODEL", ),
"image": ("IMAGE", ),
"threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"dilation": ("INT", {"default": 0, "min": 0, "max": 255, "step": 1}),
}
}
RETURN_TYPES = ("MASK",)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Legacy"
def doit(self, segm_model, image, threshold, dilation):
mmdet_results = core.inference_segm(image, segm_model, threshold)
segmasks = core.create_segmasks(mmdet_results)
if dilation > 0:
segmasks = dilate_masks(segmasks, dilation)
mask = combine_masks(segmasks)
return (mask,)
class BboxDetectorCombined(SegmDetectorCombined):
@classmethod
def INPUT_TYPES(s):
return {"required": {
"bbox_model": ("BBOX_MODEL", ),
"image": ("IMAGE", ),
"threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"dilation": ("INT", {"default": 4, "min": 0, "max": 255, "step": 1}),
}
}
def doit(self, bbox_model, image, threshold, dilation):
mmdet_results = core.inference_bbox(bbox_model, image, threshold)
segmasks = core.create_segmasks(mmdet_results)
if dilation > 0:
segmasks = dilate_masks(segmasks, dilation)
mask = combine_masks(segmasks)
return (mask,)
class SegmDetectorForEach:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"segm_model": ("SEGM_MODEL", ),
"image": ("IMAGE", ),
"threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"dilation": ("INT", {"default": 10, "min": 0, "max": 255, "step": 1}),
"crop_factor": ("FLOAT", {"default": 3.0, "min": 1.0, "max": 10, "step": 0.1}),
}
}
RETURN_TYPES = ("SEGS", )
FUNCTION = "doit"
CATEGORY = "ImpactPack/Legacy"
def doit(self, segm_model, image, threshold, dilation, crop_factor):
mmdet_results = core.inference_segm(image, segm_model, threshold)
segmasks = core.create_segmasks(mmdet_results)
if dilation > 0:
segmasks = dilate_masks(segmasks, dilation)
items = []
h = image.shape[1]
w = image.shape[2]
for x in segmasks:
item_bbox = x[0]
item_mask = x[1]
crop_region = make_crop_region(w, h, item_bbox, crop_factor)
cropped_image = crop_image(image, crop_region)
cropped_mask = crop_ndarray2(item_mask, crop_region)
confidence = x[2]
item = SEG(cropped_image, cropped_mask, confidence, crop_region, item_bbox)
items.append(item)
shape = h,w
return ((shape, items), )
class SegsMaskCombine:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"segs": ("SEGS", ),
"image": ("IMAGE", ),
}
}
RETURN_TYPES = ("MASK",)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Legacy"
@staticmethod
def combine(segs, image):
h = image.shape[1]
w = image.shape[2]
mask = np.zeros((h, w), dtype=np.uint8)
for seg in segs[1]:
cropped_mask = seg.cropped_mask
crop_region = seg.crop_region
mask[crop_region[1]:crop_region[3], crop_region[0]:crop_region[2]] |= (cropped_mask * 255).astype(np.uint8)
return torch.from_numpy(mask.astype(np.float32) / 255.0)
def doit(self, segs, image):
return (SegsMaskCombine.combine(segs, image), )
Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

-940
View File
@@ -1,940 +0,0 @@
{
"last_node_id": 77,
"last_link_id": 177,
"nodes": [
{
"id": 67,
"type": "BboxDetectorForEach",
"pos": [
738.6226098144523,
108.70101345825232
],
"size": {
"0": 290.3233947753906,
"1": 126.13687896728516
},
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "bbox_model",
"type": "BBOX_MODEL",
"link": 176
},
{
"name": "image",
"type": "IMAGE",
"link": 140
}
],
"outputs": [
{
"name": "SEGS",
"type": "SEGS",
"links": [
152
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "BboxDetectorForEach"
},
"widgets_values": [
0.5,
10,
4
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 76,
"type": "Reroute",
"pos": [
961.6226098144517,
1.701013458251873
],
"size": [
124,
26
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "",
"type": "*",
"link": 174,
"pos": [
62,
0
]
}
],
"outputs": [
{
"name": "BBOX_MODEL",
"type": "BBOX_MODEL",
"links": [
175,
176
],
"slot_index": 0
}
],
"properties": {
"showOutputText": true,
"horizontal": true
}
},
{
"id": 44,
"type": "EmptyLatentImage",
"pos": [
401.62260981445274,
110.70101345825232
],
"size": {
"0": 297.0506896972656,
"1": 124.40960693359375
},
"flags": {
"collapsed": false
},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
73
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
256,
320,
1
],
"color": "#322",
"bgcolor": "#533"
},
{
"id": 40,
"type": "VAEDecode",
"pos": [
608.6226098144529,
30.701013458251865
],
"size": {
"0": 311,
"1": 46
},
"flags": {
"collapsed": true
},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 74
},
{
"name": "vae",
"type": "VAE",
"link": 137
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
110,
140,
155
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"color": "#322",
"bgcolor": "#533"
},
{
"id": 30,
"type": "CheckpointLoader",
"pos": [
50.62260981445271,
109.70101345825233
],
"size": {
"0": 289.5052795410156,
"1": 122
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
72,
153,
168
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
49,
62
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
137,
156,
167
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoader"
},
"widgets_values": [
"anything_v3.yaml",
"V07_v07.safetensors"
],
"color": "#233",
"bgcolor": "#355"
},
{
"id": 71,
"type": "SaveImage",
"pos": [
735.4799597167967,
759.880029144287
],
"size": {
"0": 302.5051574707031,
"1": 388.0459289550781
},
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 161
}
],
"title": "Refined Image",
"properties": {},
"widgets_values": [
"refined"
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 70,
"type": "DetailerForEach",
"pos": [
733.6226098144523,
289.70101345825253
],
"size": {
"0": 297.7779235839844,
"1": 418
},
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 155
},
{
"name": "segs",
"type": "SEGS",
"link": 152
},
{
"name": "model",
"type": "MODEL",
"link": 153
},
{
"name": "vae",
"type": "VAE",
"link": 156
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 158
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 160
},
{
"name": "external_seed",
"type": "SEED",
"link": null
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
161,
172,
173
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "DetailerForEach"
},
"widgets_values": [
256,
"bbox",
650434405941391,
"randomize",
20,
8,
"euler",
"karras",
0.6,
5,
"enabled"
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 75,
"type": "SaveImage",
"pos": [
1069.4799597167953,
759.880029144287
],
"size": {
"0": 287.59619140625,
"1": 388.227783203125
},
"flags": {},
"order": 14,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 171
}
],
"title": "2nd Refined Image",
"properties": {},
"widgets_values": [
"refined"
],
"color": "#223",
"bgcolor": "#335"
},
{
"id": 41,
"type": "PreviewImage",
"pos": [
404.47995971679666,
766.880029144287
],
"size": {
"0": 300.68701171875,
"1": 383.2276916503906
},
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 110
}
],
"title": "Prompt Image",
"properties": {
"Node name for S&R": "PreviewImage"
},
"color": "#322",
"bgcolor": "#533"
},
{
"id": 37,
"type": "CLIPTextEncode",
"pos": [
45.47995971679696,
528.880029144287
],
"size": {
"0": 296.4716796875,
"1": 176.8111572265625
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 62
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
71,
158,
160,
165
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"low quality, worst quality, bad anatomy"
],
"color": "#233",
"bgcolor": "#355"
},
{
"id": 31,
"type": "CLIPTextEncode",
"pos": [
48.47995971679696,
294.8800291442872
],
"size": {
"0": 290.4716796875,
"1": 180.8111572265625
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 49
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
70,
166
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"1girl, photorealistic:1.4, masterpiece, jump, full shot, jacket, jean shorts, perfect anatomy, street city background, detailed eyes, smile"
],
"color": "#233",
"bgcolor": "#355"
},
{
"id": 42,
"type": "KSampler",
"pos": [
402.47995971679666,
291.8800291442872
],
"size": {
"0": 300.4716796875,
"1": 416.8111572265625
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 72
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 70
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 71
},
{
"name": "latent_image",
"type": "LATENT",
"link": 73
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
74
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
805157917915454,
false,
20,
8,
"euler",
"karras",
1
],
"color": "#322",
"bgcolor": "#533"
},
{
"id": 73,
"type": "BboxDetectorForEach",
"pos": [
1067.4799597167953,
107.88002914428695
],
"size": {
"0": 284.8689270019531,
"1": 126
},
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "bbox_model",
"type": "BBOX_MODEL",
"link": 175
},
{
"name": "image",
"type": "IMAGE",
"link": 172
}
],
"outputs": [
{
"name": "SEGS",
"type": "SEGS",
"links": [
164
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "BboxDetectorForEach"
},
"widgets_values": [
0.5,
10,
4
],
"color": "#223",
"bgcolor": "#335"
},
{
"id": 45,
"type": "MMDetLoader",
"pos": [
1412.1400225830075,
115.2000213623046
],
"size": {
"0": 348.3031005859375,
"1": 99.29615783691406
},
"flags": {},
"order": 2,
"mode": 0,
"outputs": [
{
"name": "BBOX_MODEL",
"type": "BBOX_MODEL",
"links": [
174
],
"slot_index": 0
},
{
"name": "SEGM_MODEL",
"type": "SEGM_MODEL",
"links": null
}
],
"properties": {
"Node name for S&R": "MMDetLoader"
},
"widgets_values": [
"bbox/mmdet_anime-face_yolov3.pth"
],
"color": "#222",
"bgcolor": "#000"
},
{
"id": 74,
"type": "DetailerForEach",
"pos": [
1068,
288
],
"size": [
285.10000305175777,
420.4000015258789
],
"flags": {},
"order": 13,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 173
},
{
"name": "segs",
"type": "SEGS",
"link": 164
},
{
"name": "model",
"type": "MODEL",
"link": 168
},
{
"name": "vae",
"type": "VAE",
"link": 167
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 166
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 165
},
{
"name": "external_seed",
"type": "SEED",
"link": null
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
171
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "DetailerForEach"
},
"widgets_values": [
256,
"bbox",
621928064748632,
"randomize",
20,
8,
"euler",
"karras",
0.6,
5,
"enabled"
],
"color": "#223",
"bgcolor": "#335"
}
],
"links": [
[
49,
30,
1,
31,
0,
"CLIP"
],
[
62,
30,
1,
37,
0,
"CLIP"
],
[
70,
31,
0,
42,
1,
"CONDITIONING"
],
[
71,
37,
0,
42,
2,
"CONDITIONING"
],
[
72,
30,
0,
42,
0,
"MODEL"
],
[
73,
44,
0,
42,
3,
"LATENT"
],
[
74,
42,
0,
40,
0,
"LATENT"
],
[
110,
40,
0,
41,
0,
"IMAGE"
],
[
137,
30,
2,
40,
1,
"VAE"
],
[
140,
40,
0,
67,
1,
"IMAGE"
],
[
152,
67,
0,
70,
1,
"SEGS"
],
[
153,
30,
0,
70,
2,
"MODEL"
],
[
155,
40,
0,
70,
0,
"IMAGE"
],
[
156,
30,
2,
70,
3,
"VAE"
],
[
158,
37,
0,
70,
4,
"CONDITIONING"
],
[
160,
37,
0,
70,
5,
"CONDITIONING"
],
[
161,
70,
0,
71,
0,
"IMAGE"
],
[
164,
73,
0,
74,
1,
"SEGS"
],
[
165,
37,
0,
74,
5,
"CONDITIONING"
],
[
166,
31,
0,
74,
4,
"CONDITIONING"
],
[
167,
30,
2,
74,
3,
"VAE"
],
[
168,
30,
0,
74,
2,
"MODEL"
],
[
171,
74,
0,
75,
0,
"IMAGE"
],
[
172,
70,
0,
73,
1,
"IMAGE"
],
[
173,
70,
0,
74,
0,
"IMAGE"
],
[
174,
45,
0,
76,
0,
"*"
],
[
175,
76,
0,
73,
0,
"BBOX_MODEL"
],
[
176,
76,
0,
67,
0,
"BBOX_MODEL"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 KiB

-658
View File
@@ -1,658 +0,0 @@
{
"last_node_id": 77,
"last_link_id": 179,
"nodes": [
{
"id": 30,
"type": "CheckpointLoader",
"pos": [
50,
230
],
"size": {
"0": 289.5052795410156,
"1": 122
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
72,
153
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
49,
62
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
137,
156
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoader"
},
"widgets_values": [
"anything_v3.yaml",
"V07_v07.safetensors"
],
"color": "#233",
"bgcolor": "#355"
},
{
"id": 67,
"type": "BboxDetectorForEach",
"pos": [
1440,
180
],
"size": {
"0": 290.3233947753906,
"1": 126.13687896728516
},
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "bbox_model",
"type": "BBOX_MODEL",
"link": 179
},
{
"name": "image",
"type": "IMAGE",
"link": 140
}
],
"outputs": [
{
"name": "SEGS",
"type": "SEGS",
"links": [
152
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "BboxDetectorForEach"
},
"widgets_values": [
0.5,
10,
4
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 40,
"type": "VAEDecode",
"pos": [
570,
180
],
"size": {
"0": 311,
"1": 46
},
"flags": {
"collapsed": true
},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 74
},
{
"name": "vae",
"type": "VAE",
"link": 137
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
110,
140,
155
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"color": "#322",
"bgcolor": "#533"
},
{
"id": 45,
"type": "MMDetLoader",
"pos": [
1030,
60
],
"size": {
"0": 361.56317138671875,
"1": 93.23355865478516
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "BBOX_MODEL",
"type": "BBOX_MODEL",
"links": [
179
],
"slot_index": 0
},
{
"name": "SEGM_MODEL",
"type": "SEGM_MODEL",
"links": null
}
],
"properties": {
"Node name for S&R": "MMDetLoader"
},
"widgets_values": [
"bbox/mmdet_anime-face_yolov3.pth"
],
"color": "#222",
"bgcolor": "#000"
},
{
"id": 71,
"type": "SaveImage",
"pos": [
1090,
360
],
"size": {
"0": 302.8558654785156,
"1": 394.98974609375
},
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 161
}
],
"title": "Refined Image",
"properties": {},
"widgets_values": [
"refined"
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 42,
"type": "KSampler",
"pos": [
400,
410
],
"size": {
"0": 295.9467468261719,
"1": 347.53521728515625
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 72
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 70
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 71
},
{
"name": "latent_image",
"type": "LATENT",
"link": 73
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
74
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
805157917915455,
"fixed",
20,
8,
"euler",
"karras",
1
],
"color": "#322",
"bgcolor": "#533"
},
{
"id": 41,
"type": "PreviewImage",
"pos": [
760,
360
],
"size": {
"0": 297.5831298828125,
"1": 395.7170104980469
},
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 110
}
],
"title": "Prompt Image",
"properties": {
"Node name for S&R": "PreviewImage"
},
"color": "#322",
"bgcolor": "#533"
},
{
"id": 44,
"type": "EmptyLatentImage",
"pos": [
400,
230
],
"size": {
"0": 297.0506896972656,
"1": 124.40960693359375
},
"flags": {
"collapsed": false
},
"order": 2,
"mode": 0,
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
73
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
256,
320,
1
],
"color": "#322",
"bgcolor": "#533"
},
{
"id": 37,
"type": "CLIPTextEncode",
"pos": [
50,
620
],
"size": [
288.77154541015625,
139.74398040771484
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 62
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
71,
158,
160
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"low quality, worst quality, bad anatomy"
],
"color": "#233",
"bgcolor": "#355"
},
{
"id": 31,
"type": "CLIPTextEncode",
"pos": [
50,
420
],
"size": {
"0": 290.0508117675781,
"1": 136.4095916748047
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 49
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
70
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"1girl, photorealistic:1.4, masterpiece, holding book, full shot, black suit, perfect anatomy, office, detailed eyes, smile"
],
"color": "#233",
"bgcolor": "#355"
},
{
"id": 70,
"type": "DetailerForEach",
"pos": [
1440,
360
],
"size": {
"0": 293.2631530761719,
"1": 442
},
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 155
},
{
"name": "segs",
"type": "SEGS",
"link": 152
},
{
"name": "model",
"type": "MODEL",
"link": 153
},
{
"name": "vae",
"type": "VAE",
"link": 156
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 158
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 160
},
{
"name": "external_seed",
"type": "SEED",
"link": null
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
161
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "DetailerForEach"
},
"widgets_values": [
192,
"bbox",
true,
"fixed",
20,
8,
"euler",
"karras",
0.4,
5,
"enabled",
"disabled"
],
"color": "#232",
"bgcolor": "#353"
}
],
"links": [
[
49,
30,
1,
31,
0,
"CLIP"
],
[
62,
30,
1,
37,
0,
"CLIP"
],
[
70,
31,
0,
42,
1,
"CONDITIONING"
],
[
71,
37,
0,
42,
2,
"CONDITIONING"
],
[
72,
30,
0,
42,
0,
"MODEL"
],
[
73,
44,
0,
42,
3,
"LATENT"
],
[
74,
42,
0,
40,
0,
"LATENT"
],
[
110,
40,
0,
41,
0,
"IMAGE"
],
[
137,
30,
2,
40,
1,
"VAE"
],
[
140,
40,
0,
67,
1,
"IMAGE"
],
[
152,
67,
0,
70,
1,
"SEGS"
],
[
153,
30,
0,
70,
2,
"MODEL"
],
[
155,
40,
0,
70,
0,
"IMAGE"
],
[
156,
30,
2,
70,
3,
"VAE"
],
[
158,
37,
0,
70,
4,
"CONDITIONING"
],
[
160,
37,
0,
70,
5,
"CONDITIONING"
],
[
161,
70,
0,
71,
0,
"IMAGE"
],
[
179,
45,
0,
67,
0,
"BBOX_MODEL"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 658 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 664 KiB

-968
View File
@@ -1,968 +0,0 @@
{
"last_node_id": 115,
"last_link_id": 282,
"nodes": [
{
"id": 82,
"type": "MMDetLoader",
"pos": [
712.5367020996093,
135.30448647521965
],
"size": {
"0": 374.412109375,
"1": 93.55003356933594
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "BBOX_MODEL",
"type": "BBOX_MODEL",
"links": [
204
],
"slot_index": 0
},
{
"name": "SEGM_MODEL",
"type": "SEGM_MODEL",
"links": [],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "MMDetLoader"
},
"widgets_values": [
"bbox/mmdet_anime-face_yolov3.pth"
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 87,
"type": "SAMLoader",
"pos": [
1109.9366837890623,
134.3044807531738
],
"size": {
"0": 327.3000183105469,
"1": 96.49998474121094
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "SAM_MODEL",
"type": "SAM_MODEL",
"links": [
282
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "SAMLoader"
},
"widgets_values": [
"sam_vit_b_01ec64.pth"
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 94,
"type": "Reroute",
"pos": [
62,
456
],
"size": [
82,
26
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "",
"type": "*",
"link": 274
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
218,
219
],
"slot_index": 0
}
],
"properties": {
"showOutputText": true,
"horizontal": false
}
},
{
"id": 103,
"type": "Reroute",
"pos": [
1409.5367020996096,
263.3044864752197
],
"size": [
75,
26
],
"flags": {},
"order": 13,
"mode": 0,
"inputs": [
{
"name": "",
"type": "*",
"link": 239
}
],
"outputs": [
{
"name": "SEGS",
"type": "SEGS",
"links": [
246
],
"slot_index": 0
}
],
"properties": {
"showOutputText": true,
"horizontal": false
}
},
{
"id": 104,
"type": "Segs & Mask",
"pos": [
1529.9366837890627,
285.3044807531738
],
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 14,
"mode": 0,
"inputs": [
{
"name": "segs",
"type": "SEGS",
"link": 246
},
{
"name": "mask",
"type": "MASK",
"link": 247
}
],
"outputs": [
{
"name": "SEGS",
"type": "SEGS",
"links": [
275
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Segs & Mask"
},
"color": "#323",
"bgcolor": "#535"
},
{
"id": 106,
"type": "Reroute",
"pos": [
872.6135858068355,
491.7605277234375
],
"size": [
75,
26
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "",
"type": "*",
"link": 252
}
],
"outputs": [
{
"name": "VAE",
"type": "VAE",
"links": [
278
],
"slot_index": 0
}
],
"properties": {
"showOutputText": true,
"horizontal": false
}
},
{
"id": 105,
"type": "CheckpointLoaderSimple",
"pos": [
21,
619
],
"size": {
"0": 315,
"1": 98
},
"flags": {},
"order": 2,
"mode": 0,
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
268,
276
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
260,
261
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
252,
267
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"V07_v07.safetensors"
]
},
{
"id": 112,
"type": "EmptyLatentImage",
"pos": [
21,
1065
],
"size": {
"0": 315,
"1": 106
},
"flags": {},
"order": 3,
"mode": 0,
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
266
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
768,
512,
1
]
},
{
"id": 108,
"type": "VAEDecode",
"pos": [
360,
1123
],
"size": {
"0": 381.7952880859375,
"1": 46.58692932128906
},
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 272
},
{
"name": "vae",
"type": "VAE",
"link": 267
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
273,
274,
277
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
}
},
{
"id": 110,
"type": "CLIPTextEncode",
"pos": [
362,
866
],
"size": {
"0": 381.7952880859375,
"1": 213.58692932128906
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 261
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
265,
280
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"nsfw, paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy, Deep Negative, (fat:1.2), facing away, looking away, tilted head, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, username, blurry, bad feet, cropped, poorly drawn hands, poorly drawn face, mutation, deformed, jpeg artifacts, signature, watermark, extra fingers, fewer digits, extra limbs, extra arms, extra legs, malformed limbs, fused fingers, too many fingers, long neck, cross-eyed, mutated hands, bad body, bad proportions, missing fingers, missing arms, missing legs, extra digit, badhandv4, EasyNegative, NG_DeepNegative_V1_75T, leather, ((solo))"
]
},
{
"id": 109,
"type": "CLIPTextEncode",
"pos": [
359,
620
],
"size": {
"0": 382,
"1": 204
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 260
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
264,
279
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"masterpiece, photorealistic:1.4, short break hair, (yellow check short jacket), white shirts, light pink necktie, (pink school check skirt), cute girl, sneakers, detailed eyes, realistic, 2girls, ultra detailed, beautiful girl, beautiful-detailed eyes, slender, small breast, smile, from top, luminous skin, stocking,"
]
},
{
"id": 107,
"type": "SaveImage",
"pos": [
752,
620
],
"size": {
"0": 703.5068359375,
"1": 549.9654541015625
},
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 273
}
],
"title": "Prompt",
"properties": {},
"widgets_values": [
"original"
]
},
{
"id": 115,
"type": "SaveImage",
"pos": [
1484,
615
],
"size": {
"0": 708.5068359375,
"1": 555.9654541015625
},
"flags": {},
"order": 16,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 281
}
],
"title": "Refined",
"properties": {},
"widgets_values": [
"refined"
]
},
{
"id": 111,
"type": "KSampler",
"pos": [
20,
762
],
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 268
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 264
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 265
},
{
"name": "latent_image",
"type": "LATENT",
"link": 266
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
272
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
832707305495685,
"fixed",
20,
7,
"euler",
"karras",
1
]
},
{
"id": 90,
"type": "BboxDetectorForEach",
"pos": [
712.9366837890625,
293.3044807531738
],
"size": {
"0": 251.412109375,
"1": 126
},
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "bbox_model",
"type": "BBOX_MODEL",
"link": 204
},
{
"name": "image",
"type": "IMAGE",
"link": 218
}
],
"outputs": [
{
"name": "SEGS",
"type": "SEGS",
"links": [
208,
239
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "BboxDetectorForEach"
},
"widgets_values": [
0.5,
20,
3
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 114,
"type": "DetailerForEach",
"pos": [
2203,
614
],
"size": {
"0": 315,
"1": 418
},
"flags": {},
"order": 15,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 277
},
{
"name": "segs",
"type": "SEGS",
"link": 275
},
{
"name": "model",
"type": "MODEL",
"link": 276
},
{
"name": "vae",
"type": "VAE",
"link": 278
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 279
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 280
},
{
"name": "external_seed",
"type": "SEED",
"link": null
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
281
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "DetailerForEach"
},
"widgets_values": [
256,
"bbox",
1041366330963730,
"randomize",
20,
7,
"euler",
"karras",
0.6,
5,
"disabled"
]
},
{
"id": 91,
"type": "SAMDetectorCombined",
"pos": [
1112.0510126953125,
289
],
"size": {
"0": 234.59999084472656,
"1": 146
},
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "sam_model",
"type": "SAM_MODEL",
"link": 282
},
{
"name": "segs",
"type": "SEGS",
"link": 208
},
{
"name": "image",
"type": "IMAGE",
"link": 219
}
],
"outputs": [
{
"name": "MASK",
"type": "MASK",
"links": [
247
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "SAMDetectorCombined"
},
"widgets_values": [
"rect-4",
10,
0.9
],
"color": "#232",
"bgcolor": "#353"
}
],
"links": [
[
204,
82,
0,
90,
0,
"BBOX_MODEL"
],
[
208,
90,
0,
91,
1,
"SEGS"
],
[
218,
94,
0,
90,
1,
"IMAGE"
],
[
219,
94,
0,
91,
2,
"IMAGE"
],
[
239,
90,
0,
103,
0,
"*"
],
[
246,
103,
0,
104,
0,
"SEGS"
],
[
247,
91,
0,
104,
1,
"MASK"
],
[
252,
105,
2,
106,
0,
"*"
],
[
260,
105,
1,
109,
0,
"CLIP"
],
[
261,
105,
1,
110,
0,
"CLIP"
],
[
264,
109,
0,
111,
1,
"CONDITIONING"
],
[
265,
110,
0,
111,
2,
"CONDITIONING"
],
[
266,
112,
0,
111,
3,
"LATENT"
],
[
267,
105,
2,
108,
1,
"VAE"
],
[
268,
105,
0,
111,
0,
"MODEL"
],
[
272,
111,
0,
108,
0,
"LATENT"
],
[
273,
108,
0,
107,
0,
"IMAGE"
],
[
274,
108,
0,
94,
0,
"*"
],
[
275,
104,
0,
114,
1,
"SEGS"
],
[
276,
105,
0,
114,
2,
"MODEL"
],
[
277,
108,
0,
114,
0,
"IMAGE"
],
[
278,
106,
0,
114,
3,
"VAE"
],
[
279,
109,
0,
114,
4,
"CONDITIONING"
],
[
280,
110,
0,
114,
5,
"CONDITIONING"
],
[
281,
114,
0,
115,
0,
"IMAGE"
],
[
282,
87,
0,
91,
0,
"SAM_MODEL"
]
],
"groups": [
{
"title": "Detection & Mask Generation",
"bounding": [
664,
13,
1190,
447
],
"color": "#3f789e"
},
{
"title": "Group",
"bounding": [
11,
496,
1462,
753
],
"color": "#3f789e"
},
{
"title": "Refine and Render",
"bounding": [
1476,
495,
1057,
753
],
"color": "#3f789e"
}
],
"config": {},
"extra": {},
"version": 0.4
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

-1424
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 665 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 658 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 577 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 814 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

File diff suppressed because it is too large Load Diff
-405
View File
@@ -1,405 +0,0 @@
{
"last_node_id": 13,
"last_link_id": 15,
"nodes": [
{
"id": 8,
"type": "VAEDecode",
"pos": [
1191,
203
],
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 9
},
{
"name": "vae",
"type": "VAE",
"link": 10
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
11
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAE Decode"
},
"color": "#223",
"bgcolor": "#335"
},
{
"id": 2,
"type": "LoadImage",
"pos": [
57,
322
],
"size": {
"0": 314,
"1": 102
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
4,
7,
14
],
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "Load Image"
},
"widgets_values": [
"rga6845.jpeg",
"image"
],
"color": "#222",
"bgcolor": "#000"
},
{
"id": 6,
"type": "VAEEncodeForInpaint",
"pos": [
942,
201
],
"size": {
"0": 226.8000030517578,
"1": 66
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "pixels",
"type": "IMAGE",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
},
{
"name": "mask",
"type": "MASK",
"link": 6
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
9
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAE Encode (for Inpainting)"
},
"color": "#223",
"bgcolor": "#335"
},
{
"id": 7,
"type": "VAELoader",
"pos": [
56,
96
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "VAE",
"type": "VAE",
"links": [
8,
10
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Load VAE"
},
"widgets_values": [
"vae-ft-mse-840000-ema-pruned.safetensors"
],
"color": "#222",
"bgcolor": "#000"
},
{
"id": 5,
"type": "SegsMaskCombine",
"pos": [
681,
369
],
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "segs",
"type": "SEGS",
"link": 15
},
{
"name": "image",
"type": "IMAGE",
"link": 4
}
],
"outputs": [
{
"name": "MASK",
"type": "MASK",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "SegsMaskCombine"
},
"color": "#322",
"bgcolor": "#533"
},
{
"id": 9,
"type": "SaveImage",
"pos": [
1476,
205
],
"size": {
"0": 475,
"1": 543
},
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 11
}
],
"properties": {},
"widgets_values": [
"ComfyUI"
],
"color": "#223",
"bgcolor": "#335"
},
{
"id": 13,
"type": "ONNXDetectorForEach",
"pos": [
405,
209
],
"size": {
"0": 210,
"1": 102
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "onnx_model",
"type": "ONNX_MODEL",
"link": 13
},
{
"name": "image",
"type": "IMAGE",
"link": 14
}
],
"outputs": [
{
"name": "SEGS",
"type": "SEGS",
"links": [
15
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ONNXDetectorForEach"
},
"widgets_values": [
0.5,
2
],
"color": "#322",
"bgcolor": "#533"
},
{
"id": 1,
"type": "ONNXLoader",
"pos": [
55,
207
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 2,
"mode": 0,
"outputs": [
{
"name": "ONNX_MODEL",
"type": "ONNX_MODEL",
"links": [
13
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ONNXLoader"
},
"widgets_values": [
"detector_v2_base_checkpoint.onnx"
],
"color": "#222",
"bgcolor": "#000"
}
],
"links": [
[
4,
2,
0,
5,
1,
"IMAGE"
],
[
6,
5,
0,
6,
2,
"MASK"
],
[
7,
2,
0,
6,
0,
"IMAGE"
],
[
8,
7,
0,
6,
1,
"VAE"
],
[
9,
6,
0,
8,
0,
"LATENT"
],
[
10,
7,
0,
8,
1,
"VAE"
],
[
11,
8,
0,
9,
0,
"IMAGE"
],
[
13,
1,
0,
13,
0,
"ONNX_MODEL"
],
[
14,
2,
0,
13,
1,
"IMAGE"
],
[
15,
13,
0,
5,
0,
"SEGS"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

-1150
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

-1089
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 KiB

+7 -1
View File
@@ -1,6 +1,7 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"id": "aaaaaaaaaa"
@@ -48,13 +49,17 @@
" !echo \"-= Updating ComfyUI =-\"\n",
" !git pull\n",
" !rm \"/content/drive/MyDrive/ComfyUI/custom_nodes/comfyui-impact-pack.py\"\n",
" !wget -O \"/content/drive/MyDrive/ComfyUI/custom_nodes/comfyui-impact-pack.py\" https://raw.githubusercontent.com/ltdrdata/ComfyUI-Impact-Pack/Main/comfyui-impact-pack.py\n",
"\n",
"%cd custom_nodes\n",
"!git clone https://github.com/ltdrdata/ComfyUI-Impact-Pack\n",
"%cd $WORKSPACE\n",
"\n",
"!echo -= Install dependencies =-\n",
"!pip -q install xformers -r requirements.txt\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"id": "kkkkkkkkkkkkkk"
@@ -103,6 +108,7 @@
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"id": "gggggggggg"
+31
View File
@@ -0,0 +1,31 @@
import onnxruntime
from impact_utils import *
def onnx_inference(image, onnx_model):
# prepare image
pil = tensor2pil(image)
image = np.ascontiguousarray(pil)
image = image[:, :, ::-1] # to BGR image
image = image.astype(np.float32)
image -= [103.939, 116.779, 123.68] # 'caffe' mode image preprocessing
# do detection
onnx_model = onnxruntime.InferenceSession(onnx_model)
outputs = onnx_model.run(
[s_i.name for s_i in onnx_model.get_outputs()],
{onnx_model.get_inputs()[0].name: np.expand_dims(image, axis=0)},
)
labels = [op for op in outputs if op.dtype == "int32"][0]
scores = [op for op in outputs if isinstance(op[0][0], np.float32)][0]
boxes = [op for op in outputs if isinstance(op[0][0], np.ndarray)][0]
# filter-out useless item
idx = np.where(labels[0] == -1)[0][0]
labels = labels[0][:idx]
scores = scores[0][:idx]
boxes = boxes[0][:idx].astype(np.uint32)
return labels, scores, boxes
+37
View File
@@ -0,0 +1,37 @@
main_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
sys.path.append(os.path.dirname(__file__))
sys.path.append(main_dir)
import server
from aiohttp import web
@server.PromptServer.instance.routes.post("/upload/temp")
async def upload_image(request):
upload_dir = folder_paths.get_temp_directory()
if not os.path.exists(upload_dir):
os.makedirs(upload_dir)
post = await request.post()
image = post.get("image")
if image and image.file:
filename = image.filename
if not filename:
return web.Response(status=400)
split = os.path.splitext(filename)
i = 1
while os.path.exists(os.path.join(upload_dir, filename)):
filename = f"{split[0]} ({i}){split[1]}"
i += 1
filepath = os.path.join(upload_dir, filename)
with open(filepath, "wb") as f:
f.write(image.file.read())
return web.json_response({"name": filename})
else:
return web.Response(status=400)
-13
View File
@@ -1,13 +0,0 @@
# NudeNet
Nudenet uses an ONNX model to detect NSFW content. This example is an application of [NudeNet](https://github.com/notAI-tech/NudeNet)'s capabilities, which detects NSFW elements in images and applies a mask as a post-processing step. This technique demonstrates the use of nudenet to detect potentially inappropriate content in order to ensure the safety of minors on certain websites
* [detector_v2_base_checkpoint.onnx](https://github.com/notAI-tech/NudeNet/releases/download/v0/detector_v2_base_checkpoint.onnx) is a model for nudenet. Download and placed into **ComfyUI/models/onnx**
* [onnx.json](../misc/onnx.json) is a workflow of this example.
![nudenet](nudenet.png)
# ONNX Models
* You can download various ONNX models from [here](https://github.com/PINTO0309/PINTO_model_zoo).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 KiB

-22
View File
@@ -1,22 +0,0 @@
#### 1. Basic auto face detection and refine exapmle.
![example](advanced-simple.png)
![example](advanced-simple-original.png) ![example](advanced-simple-refined.png) ![example](advanced-simple-refined-noisemask-disabled.png) ![example](advanced-simple-refined-noisemask-enabled.png)
* You can load models for bbox or segm using MMDetLoader. If you load a bbox model, only **BBOX_MODEL** is valid in the output, and if you load a segm model, only **SEGM_MODEL** is valid.
* Currently, Impact Pack is providing the more sophisticated SAM model instead of the SEGM_MODEL for silhouette extraction.
* The default downloaded bbox model currently only detects the face area as a rectangle, and the segm model detects the silhouette of a person.
* The difference between BboxDetectorCombine and BboxDetectorForEach is that the former outputs a single mask by combining all detected bboxes, and the latter outputs SEGS consisting of various information, including the cropped image, mask pattern, crop position, and confidence, for each detection. SEGS can be used in other ...ForEach nodes.
* The "noise_mask" option determines whether to add noise only to the masked area when generating an image using "KSampler". If enabled, denoising will not be applied outside the masked area, which can result in a safer generation with stronger denoising, but it may not always produce good results. The middle image shows the result when the "noise_mask" option is disabled, and the image on the right shows the result when the "noise_mask" option is enabled.
* Detector Node
* threshold: Detect only those object whose recognized confidence is above this set value.
* dilation: Expand the detected mask area.
* crop_factor: Determine how many times the surrounding area should be included in the detail recovery process based on the detected mask area. If this value is small, the restoration may not work well because the surrounding context cannot be known.
* Detailer Node
* guide_size: This feature attempt detail recovery only when the size of the detected mask is smaller than this value. If the size is larger, this feature increase the resolution and attempt detail recovery.
* guide_size_for: This parameter determines whether guide_size is used based on the size of the detected face (bbox) or the size of the crop area that includes the face and is broadly cropped by crop_factor.
* feather: When compositing the recovered details onto the original image, this feature use a gradient to composite it so that the boundaries are not visible. The thickness of this gradient is determined.
* force_inpaint: force_inpaint will try to force regeneration even if it is smaller than guide_size . This function is useful when you simply want to change to another type of prompt other than the function to save details. However, in this case, upscale is forcibly fixed to 1.
* This feature adopt the properties of KSampler because this feature use it to recover details.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

-13
View File
@@ -1,13 +0,0 @@
# Mask Pointer
![maskpointer](maskpointer.png)
* When setting the detection-hint as **mask-points** in SAMDetector, multiple mask fragments are provided as SAM prompts. If using **mask-area**, only some of the points within the float mask's inner area are provided as SAM prompts. When detection_hint_use_negative is set to True, very small dots are interpreted as negative prompts in mask-points, and some areas with a mask value of 0 are interpreted as negative prompts in **mask-area**. The **detection_hint_threshold** interprets cases where the mask value in **mask-area** is equal to or higher than the threshold as positive prompts. However, values greater than 0 but less than the **detection_hint_threshold** are not used as negative prompts.
* When using **mask-points**, please note that you should set "combined" parameter as **False** in MaskToSegs.
![maskpointer](pointer.png)
* When you adjust the mouse wheel up and down, you can adjust the size of the brush. When you hold down the left mouse button and drag, it will draw, and when you hold down the right mouse button and drag, it will erase.
* When using the mask_hint_use_negative as true in conjunction with the mask-points mode, large dots are interpreted as positive prompts and small dots are interpreted as negative prompts.
<img src="maskpointer-original.png" width=30% height=30%> <img src="maskpointer-shirt.png" width=30% height=30%> <img src="maskpointer-hair.png" width=30% height=30%>
* By using **mask-points** or **mask-area**, you can select only certain parts, such as shirts or hair, to regenerate.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB