Files
mcDandy-more_math/more_math/ClipMathNode.py
T

109 lines
4.1 KiB
Python

from comfy_api.latest import io
from inspect import cleandoc
from antlr4 import InputStream, CommonTokenStream
from .Parser.MathExprLexer import MathExprLexer
from .Parser.MathExprParser import MathExprParser
import re
from .Stack import MrmthStack
import copy
import comfy.utils
class CLIPMathNode(io.ComfyNode):
"""
This node enables the use of math expressions on CLIP weights using Autogrow inputs.
"""
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id="mrmth_ag_CLIPMathNode",
display_name="CLIP Math",
category="More math",
inputs=[
io.Autogrow.Input(id="V",template=io.Autogrow.TemplatePrefix(io.Clip.Input("values"), prefix="V", min=1, max=50)),
io.Autogrow.Input(id="F", template=io.Autogrow.TemplatePrefix(io.Float.Input("float", default=0.0, optional=True, lazy=True, force_input=True), prefix="F", min=1, max=50)),
io.String.Input(id="Expression", default="I0*(1-F0)+I1*F0", tooltip="Expression to apply on weights"),
io.Combo.Input(
id="length_mismatch",
options=["do nothing","error","tile", "pad"],
display_name="on size mismatch",
default="error",
tooltip="How to handle mismatched layer counts. For models, this usually defaults to broadcast (zero for missing layers)."
),
MrmthStack.Input(id="stack", tooltip="Access stack between nodes",optional=True)
],
outputs=[
io.Clip.Output(),
MrmthStack.Output(),
],
)
tooltip = cleandoc(__doc__)
@classmethod
def check_lazy_status(cls, Expression, V, F, length_mismatch="tile",stack={}):
input_stream = InputStream(Expression)
lexer = MathExprLexer(input_stream)
stream = CommonTokenStream(lexer)
stream.fill()
# Support aliases
aliases_img = {"a": "V0", "b": "V1", "c": "V2", "d": "V3"}
aliases_flt = {"w": "F0", "x": "F1", "y": "F2", "z": "F3"}
needed = []
needed1 = []
for token in filter(lambda t: t.type == MathExprParser.VARIABLE, stream.tokens):
var_name = token.text
if re.match(r"[VF][0-9]+", var_name):
needed.append(var_name)
elif var_name in aliases_img:
needed.append(aliases_img[var_name])
elif var_name in aliases_flt:
needed.append(aliases_flt[var_name])
for v in needed:
if v.startswith("V"):
if v not in V or V[v] is None:
needed1.append(v)
elif v.startswith("F"):
if v not in F or F[v] is None:
needed1.append(v)
return needed1
@classmethod
def execute(cls, V, F, Expression, length_mismatch="tile",stack={}) -> io.NodeOutput:
# Determine reference CLIP
a = V.get("V0")
stack = copy.deepcopy(stack) if stack is not None else {}
if a is None:
for m in V.values():
if m is not None:
a = m
break
if a is None:
raise ValueError("At least one input CLIP is required.")
# Prepare CLIP patchers
patchers_V = {}
for k, v in V.items():
if v is not None:
patchers_V[k] = v.patcher
# Call autogrow patch calculation
from .modelLikeCommon import calculate_patches_autogrow
layer_count = V.get("V0").model.state_dict().__len__() if hasattr(V.get("V0"), "model") and hasattr(V.get("V0").model, "state_dict") else 0
pbar = comfy.utils.ProgressBar(layer_count)
aliases = {"a": "V0", "b": "V1", "c": "V2", "d": "V3", "w": "F0", "x": "F1", "y": "F2", "z": "F3"}
patches = calculate_patches_autogrow(Expression, V=patchers_V, F=F,pbar=pbar, mapping=aliases,stack=stack)
out_clip = a.clone()
if patches:
out_clip.add_patches(patches, 1.0, 1.0)
return (out_clip,stack)