Files
DemonGatanjieu-Anomalous_Mo…/api/reference_blocks.py
T
DemonGatanjieuandClaude Opus 5.5 6ce8af60cd Repaint block: the margin the source used, denoise 0.75, an area prompt
A Flash exam showed the crop-and-stitch repaint painting a whole new
person into the hand: the context margin was 1.2 where the distilled
workflow used 2.0, and the sampler kept denoise 1 and the whole
picture's prompt. A block can now set settings on the canvas nodes it
finds (roles: the sampler's denoise 0.75, in the same Ctrl+Z step),
and the block's answer and the repaint note tell the AI to set the
prompt to what belongs in the area. Retake: a natural hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:22:21 +08:00

132 lines
8.3 KiB
Python

"""Ready-made blocks of nodes, distilled from workflows that do one thing well (a repaint that
crops around the marked area and stitches it back, face and hand detailing): a light model
names one (add_nodes block=…) and Anomalous builds it on the canvas, spliced into the
workflow there. A block is a group in ComfyUI's API form (mcp_canvas_edit.add_nodes) whose
links may name roles the page finds on the canvas by their sockets (web/modules/canvas_roles.js):
"$sampler" (the KSampler), "$decode" (the node decoding its latent), "$save" (the node taking
that picture); ["$decode", 0] is that node's output, ["$sampler.positive", "*"] what feeds that
input, a path ("$sampler.positive.clip") followed upstream. The directory (directory()) is the
"reference" the AI is handed when it begins a task: one line a block, the rest comes with the block.
"""
BLOCKS = {
"inpaint_crop_stitch": {
"for": "Repaint a marked area (inpaint_image) of a picture: crops around it, repaints it at 1024 and stitches "
"it back, the rest untouched. Best for faces, hands, small fixes.",
"packs": {"InpaintCropImproved": "ComfyUI-Inpaint-CropAndStitch", "InpaintStitchImproved": "ComfyUI-Inpaint-CropAndStitch"},
"instead": "inpaint_basic",
"takes": "image",
"nodes": {
"picture": {"class_type": "LoadImage", "inputs": {"image": "$image"}},
"soft": {"class_type": "DifferentialDiffusion", "inputs": {"model": ["$sampler.model", "*"]}, "wire_to": "$sampler"},
"crop": {"class_type": "InpaintCropImproved", "inputs": {
"image": ["picture", 0], "mask": ["picture", 1], "mask_blend_pixels": 32, "context_from_mask_extend_factor": 2.0,
"output_resize_to_target_size": True, "output_target_width": 1024, "output_target_height": 1024,
"downscale_algorithm": "lanczos", "upscale_algorithm": "lanczos"}},
"condition": {"class_type": "InpaintModelConditioning", "inputs": {
"vae": ["$decode.vae", "*"], "pixels": ["crop", "cropped_image"], "mask": ["crop", "cropped_mask"]},
"wire_to": "$sampler"},
"stitch": {"class_type": "InpaintStitchImproved", "inputs": {"stitcher": ["crop", "stitcher"], "inpainted_image": ["$decode", 0]},
"wire_to": "$save"},
},
"roles": {"sampler": {"denoise": 0.75}}, # at 1 a model not made for inpainting paints a whole new subject into the crop
"after": "The sampler now repaints only the cropped area, at denoise 0.75 (lower keeps more of the old). Its "
"prompt is still the whole picture's: set_prompt it to what belongs in the area (\"a hand, five "
"fingers\"), or it paints the whole subject there. Then run_workflow; the saved picture is the whole "
"one, stitched.",
},
"inpaint_basic": {
"for": "Repaint a marked area (inpaint_image) with ComfyUI's own nodes only: the whole picture is sampled, "
"only the area changes.",
"packs": {},
"takes": "image",
"nodes": {
"picture": {"class_type": "LoadImage", "inputs": {"image": "$image"}},
"soft": {"class_type": "DifferentialDiffusion", "inputs": {"model": ["$sampler.model", "*"]}, "wire_to": "$sampler"},
"encode": {"class_type": "VAEEncode", "inputs": {"pixels": ["picture", 0], "vae": ["$decode.vae", "*"]}},
"area": {"class_type": "SetLatentNoiseMask", "inputs": {"samples": ["encode", 0], "mask": ["picture", 1]},
"wire_to": "$sampler"},
},
"roles": {"sampler": {"denoise": 0.7}},
"after": "The sampler repaints only the area, at denoise 0.7 so it keeps the picture's look (higher changes more). "
"Then run_workflow.",
},
"detail_face_hands": {
"for": "Fix faces and hands after a picture is made: finds each one, repaints it larger and puts it back "
"(FaceDetailer). For a picture made on this canvas.",
"packs": {"FaceDetailer": "ComfyUI-Impact-Pack", "UltralyticsDetectorProvider": "ComfyUI-Impact-Subpack"},
"detectors": {"face": "face_yolov8", "hand": "hand_yolov8"},
"nodes": {
"face_finder": {"class_type": "UltralyticsDetectorProvider", "inputs": {"model_name": "$face"}},
"hand_finder": {"class_type": "UltralyticsDetectorProvider", "inputs": {"model_name": "$hand"}},
"face": {"class_type": "FaceDetailer", "inputs": {
"image": ["$decode", 0], "model": ["$sampler.model", "*"], "clip": ["$sampler.positive.clip", "*"],
"vae": ["$decode.vae", "*"], "positive": ["$sampler.positive", "*"], "negative": ["$sampler.negative", "*"],
"bbox_detector": ["face_finder", 0], "denoise": 0.4, "guide_size": 512}},
"hands": {"class_type": "FaceDetailer", "inputs": {
"image": ["face", "image"], "model": ["$sampler.model", "*"], "clip": ["$sampler.positive.clip", "*"],
"vae": ["$decode.vae", "*"], "positive": ["$sampler.positive", "*"], "negative": ["$sampler.negative", "*"],
"bbox_detector": ["hand_finder", 0], "denoise": 0.45, "guide_size": 512}},
"save": {"class_type": "SaveImage", "inputs": {"images": ["hands", "image"], "filename_prefix": "detailed"}},
},
"after": "run_workflow saves the picture as before and the detailed one beside it (prefix detailed).",
},
}
class BlockError(ValueError):
pass
def directory(info=None):
"""{block: what it is for} for the AI; with ComfyUI's node list `info`, a block whose nodes are missing says what to install."""
out = {}
for name, block in BLOCKS.items():
missing = sorted({pack for kind, pack in block["packs"].items() if info is not None and kind not in info})
out[name] = block["for"] + (f" Needs {', '.join(missing)} (not installed: use {block['instead']})."
if missing and block.get("instead") else f" Needs {', '.join(missing)} (not installed)." if missing else "")
return out
def _detector(info, prefix):
"""The detector model file for `prefix` this ComfyUI has, preferring the larger kinds (m over s over n)."""
try:
files = info["UltralyticsDetectorProvider"]["input"]["required"]["model_name"][0]
except (KeyError, IndexError, TypeError):
files = []
found = sorted((name for name in files if isinstance(name, str) and prefix in name.lower()),
key=lambda name: ("m." not in name, "s." not in name, name))
if not found:
raise BlockError(f"No {prefix.split('_')[0]} detector model in models/ultralytics/bbox ({prefix}*.pt): "
"ask the user to get one (ComfyUI-Manager's model list has them).")
return found[0]
def nodes_of(name, info, image=None):
"""Block `name` as add_nodes' group, its values filled in, or BlockError saying why not."""
block = BLOCKS.get(name)
if not block:
raise BlockError(f"No block {name!r}; there are: {', '.join(BLOCKS)}.")
missing = sorted({pack for kind, pack in block["packs"].items() if kind not in info})
if missing:
instead = f" Use block {block['instead']} instead, or ask the user to install it with ComfyUI-Manager." if block.get("instead") else \
" Ask the user to install it with ComfyUI-Manager."
raise BlockError(f"Block {name} needs {', '.join(missing)}, not installed here.{instead}")
if block.get("takes") == "image" and not (isinstance(image, str) and image.strip()):
raise BlockError(f"Block {name} needs image: the picture with the area as its alpha (inpaint_image from "
"propose_repaint_area or ask_user's answer).")
fills = {"$image": (image or "").strip()}
for role, prefix in (block.get("detectors") or {}).items():
fills[f"${role}"] = _detector(info, prefix)
return {key: {**spec, "inputs": {input: fills.get(value, value) if isinstance(value, str) else value
for input, value in spec["inputs"].items()}} for key, spec in block["nodes"].items()}
def roles_of(name):
"""The settings block `name` changes on canvas nodes it finds ({role: {setting: value}}), as the page applies them."""
return BLOCKS[name].get("roles") or {}
def after(name):
return BLOCKS[name]["after"]