diff --git a/README.md b/README.md index e0b9c14..a11fe6b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A ComfyUI nodes collection... eventually. 4. Allow discarding penultimate sigma (look for the `BlehDiscardPenultimateSigma` node). This can be useful if you find certain samplers are ruining your image by spewing a bunch of noise into it at the very end (usually only an issue with `dpm2 a` or SDE samplers). 5. Allow more conveniently switching between samplers during sampling (look for the [BlehInsaneChainSampler](#blehinsanechainsampler) node). 6. Apply arbitrary model patches at an interval and/or for a percentage of sampling (look for the [BlehModelPatchConditional](#blehmodelpatchconditional) node). -7. Ensure a seed is set even when `add_noise` is turned off in a sampler. Yes, that's right: if you don't have `add_noise` enabled _no_ seed gets set for samplers like `euler_a` and it's not possible to reproduce generations. (look for the [BlehForceSeedSampler](#blehforceseedsampler) node) +7. Ensure a seed is set even when `add_noise` is turned off in a sampler. Yes, that's right: if you don't have `add_noise` enabled _no_ seed gets set for samplers like `euler_a` and it's not possible to reproduce generations. (look for the [BlehForceSeedSampler](#blehforceseedsampler) node). For `SamplerCustomAdvanced` you can use `BlehDisableNoise` to accomplish the same thing. 8. Allows swapping to a refiner model at a predefined time (look for the [BlehRefinerAfter](#blehrefinerafter) node). 9. Allow defining arbitrary model patches (look for the [BlehBlockOps](#blehblockops) node). @@ -102,6 +102,14 @@ This is basically the same as chaining a bunch of samplers together and manually Currently, the way ComfyUI's advanced and custom samplers work is if you turn off `add_noise` _no_ global RNG seed gets set. Samplers like `euler_a` use this (SDE samplers use a different RNG method and aren't subject to this issue). Anyway, the upshot is you will get a different generation every time regardless of what the seed is set to. This node simply wraps another sampler and ensures that the seed gets set. +### BlehDisableNoise + +Basically the same idea as `BlehForceSeedSampler`, however it is usable with `SamplerCustomAdvanced`. + + +### BlehPlug + +You can connect this node to any input and it will be the same as if the input had no connection. Why is this useful? It's mainly for [Use Everywhere](https://github.com/chrisgoringe/cg-use-everywhere) — sometimes it's desirable to leave an input unconnected, but if you have Use Everywhere broadcasting an output it can be inconvenient. Just shove a plug in those inputs. ### BlehRefinerAfter diff --git a/__init__.py b/__init__.py index 7a545a7..5eae490 100644 --- a/__init__.py +++ b/__init__.py @@ -8,17 +8,19 @@ if settings.SETTINGS.btp_enabled: from .py.nodes import ( deepShrink, hyperTile, + misc, modelPatchConditional, ops, refinerAfter, samplers, - sigmas, ) NODE_CLASS_MAPPINGS = { "BlehBlockOps": ops.BlehBlockOps, "BlehDeepShrink": deepShrink.DeepShrinkBleh, - "BlehDiscardPenultimateSigma": sigmas.DiscardPenultimateSigma, + "BlehDiscardPenultimateSigma": misc.DiscardPenultimateSigma, + "BlehDisableNoise": misc.BlehDisableNoise, + "BlehPlug": misc.BlehPlug, "BlehForceSeedSampler": samplers.BlehForceSeedSampler, "BlehHyperTile": hyperTile.HyperTileBleh, "BlehInsaneChainSampler": samplers.BlehInsaneChainSampler, diff --git a/changelog.md b/changelog.md index 778ba23..c45f80b 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,11 @@ Note, only relatively significant changes to user-visible functionality will be included here. Most recent changes at the top. +## 20240423 + +* Added `BlehPlug` and `BlehDisableNoise` (see README for usage and description). +* Increased the available upscale/downscale types for `BlehDeepShrink`. + ## 20240412 * Added `BlehBlockOps` and `BlehLatentOps` nodes. diff --git a/py/nodes/deepShrink.py b/py/nodes/deepShrink.py index b8b0b95..a611e10 100644 --- a/py/nodes/deepShrink.py +++ b/py/nodes/deepShrink.py @@ -2,8 +2,7 @@ import bisect -import torch -from comfy.utils import bislerp +from .. import latent_utils # noqa: TID252 class DeepShrinkBleh: @@ -47,8 +46,8 @@ class DeepShrinkBleh: {"default": 0.35, "min": 0.0, "max": 1.0, "step": 0.001}, ), "downscale_after_skip": ("BOOLEAN", {"default": True}), - "downscale_method": (cls.upscale_methods,), - "upscale_method": (cls.upscale_methods,), + "downscale_method": (latent_utils.UPSCALE_METHODS,), + "upscale_method": (latent_utils.UPSCALE_METHODS,), "antialias_downscale": ("BOOLEAN", {"default": False}), "antialias_upscale": ("BOOLEAN", {"default": False}), }, @@ -135,29 +134,23 @@ class DeepShrinkBleh: ) if scaled_scale >= 0.98 or width >= orig_width or height >= orig_height: return h - if downscale_method == "bislerp": - return bislerp(h, width, height) - return torch.nn.functional.interpolate( + return latent_utils.scale_samples( h, - size=(height, width), + width, + height, mode=downscale_method, - antialias=antialias_downscale, + antialias_size=3 if antialias_downscale else 0, ) def output_block_patch(h, hsp, _transformer_options): if h.shape[2] == hsp.shape[2]: return h, hsp - if upscale_method == "bislerp": - return bislerp( - h, - hsp.shape[-1], - hsp.shape[-2], - ), hsp - return torch.nn.functional.interpolate( + return latent_utils.scale_samples( h, - size=(hsp.shape[-2], hsp.shape[-1]), + hsp.shape[-1], + hsp.shape[-2], mode=upscale_method, - antialias=antialias_upscale, + antialias_size=3 if antialias_upscale else 0, ), hsp m = model.clone() diff --git a/py/nodes/misc.py b/py/nodes/misc.py new file mode 100644 index 0000000..b069bca --- /dev/null +++ b/py/nodes/misc.py @@ -0,0 +1,82 @@ +import random + +import torch + + +class DiscardPenultimateSigma: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "enabled": ("BOOLEAN", {"default": True}), + "sigmas": ("SIGMAS", {"forceInput": True}), + }, + } + + FUNCTION = "go" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "sampling/custom_sampling/sigmas" + + def go(self, enabled, sigmas): + if not enabled or len(sigmas) < 2: + return (sigmas,) + return (torch.cat((sigmas[:-2], sigmas[-1:])),) + + +class SeededDisableNoise: + def __init__(self, seed): + self.seed = seed + + def generate_noise(self, latent): + samples = latent["samples"] + torch.manual_seed(self.seed) + random.seed(self.seed) # For good measure. + return torch.zeros( + samples.shape, + dtype=samples.dtype, + layout=samples.layout, + device="cpu", + ) + + +class BlehDisableNoise: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "noise_seed": ( + "INT", + {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF}, + ), + }, + } + + def go(self, noise_seed): + return (SeededDisableNoise(noise_seed),) + + RETURN_TYPES = ("NOISE",) + FUNCTION = "go" + CATEGORY = "sampling/custom_sampling/noise" + + +class Wildcard(str): + __slots__ = () + + def __ne__(self, _unused): + return False + + +class BlehPlug: + WILDCARD = Wildcard("*") + + @classmethod + def INPUT_TYPES(cls): + return {} + + def go(self): + return (None,) + + RETURN_TYPES = (WILDCARD,) + FUNCTION = "go" + OUTPUT_NODE = False + CATEGORY = "hacks" diff --git a/py/nodes/sigmas.py b/py/nodes/sigmas.py deleted file mode 100644 index fcbb335..0000000 --- a/py/nodes/sigmas.py +++ /dev/null @@ -1,21 +0,0 @@ -import torch - - -class DiscardPenultimateSigma: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "enabled": ("BOOLEAN", {"default": True}), - "sigmas": ("SIGMAS", {"forceInput": True}), - }, - } - - FUNCTION = "go" - RETURN_TYPES = ("SIGMAS",) - CATEGORY = "sampling/custom_sampling/sigmas" - - def go(self, enabled, sigmas): - if not enabled or len(sigmas) < 2: - return (sigmas,) - return (torch.cat((sigmas[:-2], sigmas[-1:])),)