| |
| """Generate flux-v2 workflow JSONs: portrait pre-pass, saved crops, hires group, bfl guidance fix.""" |
| import build_workflows as B |
| from build_workflows import G, DEFS, CORE, BYPASS, flux_loaders, redux_branch |
|
|
| ESSENTIALS = {"cnr_id": "comfyui_essentials"} |
| DEFS["ImageResize+"] = ([("image", "IMAGE")], |
| [("IMAGE", "IMAGE"), ("width", "INT"), ("height", "INT")], |
| ESSENTIALS, [310, 170]) |
| DEFS["LatentUpscaleBy"] = ([("samples", "LATENT")], [("LATENT", "LATENT")], CORE, [290, 82]) |
|
|
| W, H = 896, 1152 |
| HW, HH = 1344, 1728 |
|
|
| INPUT_SPEC = """ |
| **Optimal inputs** (both refs): 3:4 portrait, 1152x1536, sRGB 8-bit JPEG q90+/PNG, no |
| watermarks/text/borders, EXIF baked. Style ref: keep the style-defining content in the vertical |
| middle (sigclip center-crops to a square β top/bottom ~12% are discarded). Composition ref: |
| clear fg/bg depth separation. Any other input still works β the pre-pass normalizes to 896x1152 |
| (fill/crop) and the exact crop + depth map are saved next to the outputs for auditing.""" |
|
|
|
|
| def comp_prepass(g, x, y, save_prefix): |
| """LoadImage -> ImageResize+ 896x1152 fill/crop -> depth; saves crop + depth map.""" |
| ci = g.add("LoadImage", ["composition_ref.png", "image"], pos=(x, y), |
| title="Composition reference (Image B)") |
| rs = g.add("ImageResize+", [W, H, "lanczos", "fill / crop", "always", 0], |
| inputs={"image": (ci, 0)}, pos=(x + 420, y + 30), title="Normalize to 896x1152") |
| g.add("SaveImage", [f"{save_prefix}/inputs/comp-crop"], inputs={"images": (rs, 0)}, |
| pos=(x + 420, y + 240), size=[260, 270], title="Save crop (audit)") |
| depth = g.add("DepthAnythingV2Preprocessor", ["depth_anything_v2_vitl.pth", W], |
| inputs={"image": (rs, 0)}, pos=(x + 840, y + 30)) |
| g.add("SaveImage", [f"{save_prefix}/inputs/depth"], inputs={"images": (depth, 0)}, |
| pos=(x + 840, y + 240), size=[260, 270], title="Save depth map (audit)") |
| return ci, rs, depth |
|
|
|
|
| def build_v2_style_composition(): |
| g = G() |
| g.add("MarkdownNote", [f"""## FLUX Redux + ControlNet β style + composition (v2) |
| |
| v2 changes vs v1: portrait pre-pass (any input β exact **896x1152** fill/crop, so depth map == |
| latent, zero padding), saved input crops + depth maps for auditing, steps 32, and a bypassed |
| **HIRES** group (1.5x latent upscale β second pass at denoise 0.30 with Redux conditioning |
| carried, ControlNet released β enable for ~1344x1728 finals; keeps aesthetic, adds detail). |
| |
| Models & knobs: unchanged from v1 (see `flux-redux-style-composition.json` notes). Redux **multiply 0.35** β NEVER attn_bias with a ControlNet: its attention mask never reaches |
| the CN branch (code-verified), so base and CN fight and bodies deform. Union-Pro-2.0 depth |
| 0.7 / end 0.8 (drop end to 0.6 for more freedom), FluxGuidance **3.0** (sweep-validated; 2.5 softer/filmic, 3.5 punchier), euler/simple/32/CFG 1. |
| Bypassed groups: CANNY stack (0.35), ReduxAdvanced alt, turbo fast-preview, HIRES. |
| {INPUT_SPEC}"""], pos=(-1340, -180), size=[580, 620]) |
|
|
| unet, clip, vae = flux_loaders(g) |
| s_img, cv_loader, cv_enc, sm_loader = redux_branch(g, -720, 420) |
| txt = g.add("CLIPTextEncode", ['A photograph of a beautiful woman posing naturally. Her body is anatomically correct, with well-proportioned limbs, natural relaxed hands, and a balanced, graceful posture. The image is sharp and coherent, with realistic skin texture and lighting that matches the scene.'], inputs={"clip": (clip, 0)}, pos=(-300, 60), |
| title="Prompt (generic anatomy default β edit or clear)") |
| guid = g.add("FluxGuidance", [3.0], inputs={"conditioning": (txt, 0)}, pos=(140, 60)) |
| neg = g.add("CLIPTextEncode", ['deformed anatomy, extra limbs, missing limbs, fused or extra fingers, malformed hands, twisted joints, distorted face, mutated body, disfigured, blurry, lowres, jpeg artifacts, watermark, text, logo, oversaturated, plastic skin'], inputs={"clip": (clip, 0)}, pos=(-300, 250), |
| title="Negative (INERT at CFG 1.0 β activates only if CFG > 1)") |
| apply_style = g.add("StyleModelApply", [0.35, "multiply"], |
| inputs={"conditioning": (guid, 0), "style_model": (sm_loader, 0), |
| "clip_vision_output": (cv_enc, 0)}, pos=(140, 300)) |
| radv = g.add("ReduxAdvanced", [3, "area", "center crop (square)", 1.0, 0.1], |
| inputs={"conditioning": (guid, 0), "style_model": (sm_loader, 0), |
| "clip_vision": (cv_loader, 0), "image": (s_img, 0)}, |
| pos=(140, 520), mode=BYPASS, title="ALT: ReduxAdvanced (rewire to use)") |
|
|
| ci, rs, depth = comp_prepass(g, -720, 1000, "flux-v2/style-comp") |
| canny = g.add("Canny", [0.2, 0.5], inputs={"image": (rs, 0)}, pos=(-300, 1560), |
| mode=BYPASS, title="OPTIONAL: canny edges") |
| cn_loader = g.add("ControlNetLoader", ["FLUX.1-dev-ControlNet-Union-Pro-2.0.safetensors"], |
| pos=(300, 950)) |
| cn_depth = g.add("ControlNetApplySD3", [0.7, 0.0, 0.8], |
| inputs={"positive": (apply_style, 0), "negative": (neg, 0), |
| "control_net": (cn_loader, 0), "vae": (vae, 0), "image": (depth, 0)}, |
| pos=(700, 300), title="Apply ControlNet β DEPTH") |
| cn_canny = g.add("ControlNetApplySD3", [0.35, 0.0, 0.6], |
| inputs={"positive": (cn_depth, 0), "negative": (cn_depth, 1), |
| "control_net": (cn_loader, 0), "vae": (vae, 0), "image": (canny, 0)}, |
| pos=(700, 560), mode=BYPASS, title="OPTIONAL: Apply ControlNet β CANNY (stack)") |
|
|
| turbo = g.add("LoraLoaderModelOnly", ["flux1-turbo-alpha.safetensors", 1.0], |
| inputs={"model": (unet, 0)}, pos=(-300, -160), mode=BYPASS, |
| title="FAST PREVIEW: turbo LoRA (set steps 8)") |
| msf = g.add("ModelSamplingFlux", [1.15, 0.5, W, H], inputs={"model": (turbo, 0)}, pos=(140, -160)) |
| latent = g.add("EmptySD3LatentImage", [W, H, 1], pos=(700, 950)) |
| ks = g.add("KSampler", [0, "randomize", 32, 1.0, "euler", "simple", 1.0], |
| inputs={"model": (msf, 0), "positive": (cn_canny, 0), "negative": (cn_canny, 1), |
| "latent_image": (latent, 0)}, pos=(1120, 300)) |
| dec = g.add("VAEDecode", inputs={"samples": (ks, 0), "vae": (vae, 0)}, pos=(1460, 300)) |
| g.add("SaveImage", ["flux-v2/style-comp"], inputs={"images": (dec, 0)}, pos=(1460, 420)) |
|
|
| |
| up = g.add("LatentUpscaleBy", ["bislerp", 1.5], inputs={"samples": (ks, 0)}, |
| pos=(1120, 660), mode=BYPASS, title="HIRES: 1.5x latent") |
| msf2 = g.add("ModelSamplingFlux", [1.15, 0.5, HW, HH], inputs={"model": (turbo, 0)}, |
| pos=(1120, 800), mode=BYPASS, title="HIRES: shift @1344x1728") |
| ks2 = g.add("KSampler", [0, "randomize", 32, 1.0, "euler", "simple", 0.30], |
| inputs={"model": (msf2, 0), "positive": (apply_style, 0), "negative": (neg, 0), |
| "latent_image": (up, 0)}, pos=(1460, 660), mode=BYPASS, |
| title="HIRES: refine pass (denoise 0.30)") |
| dec2 = g.add("VAEDecode", inputs={"samples": (ks2, 0), "vae": (vae, 0)}, |
| pos=(1800, 660), mode=BYPASS) |
| g.add("SaveImage", ["flux-v2/style-comp-hires"], inputs={"images": (dec2, 0)}, |
| pos=(1800, 780), mode=BYPASS, title="HIRES: save") |
|
|
| g.group("STYLE REFERENCE β Redux", -740, 340, 1220, 700, "#3f789e") |
| g.group("COMPOSITION REFERENCE β 896x1152 pre-pass", -740, 900, 1500, 840, "#8f5b34") |
| g.group("SAMPLING", 1080, 200, 800, 380, "#444") |
| g.group("HIRES (bypassed β enable all 5)", 1080, 600, 1100, 520, "#4a6b4a") |
| g.dump(f"{B.OUT}/flux-v2-redux-style-composition.json") |
|
|
|
|
| def build_v2_bfl_lora(): |
| g = G() |
| g.add("MarkdownNote", [f"""## FLUX Redux + official BFL Depth LoRA (v2 β A/B variant) |
| |
| v2 changes vs v1: portrait pre-pass (896x1152 fill/crop β critical here: with latent-concat |
| conditioning any padding becomes image content), saved crop + depth map, steps 32, and |
| **FluxGuidance 10.0 + LoRA 0.85** (BFL spec β the LoRA is distilled at guidance 10; earlier |
| lower-guidance results were an artifact of the attn_bias bug). Redux multiply 0.35, never |
| attn_bias (see style-composition notes). No canny stacking possible in this method. |
| {INPUT_SPEC}"""], pos=(-1340, -180), size=[580, 560]) |
|
|
| unet, clip, vae = flux_loaders(g) |
| s_img, cv_loader, cv_enc, sm_loader = redux_branch(g, -720, 420) |
| txt = g.add("CLIPTextEncode", ['A photograph of a beautiful woman posing naturally. Her body is anatomically correct, with well-proportioned limbs, natural relaxed hands, and a balanced, graceful posture. The image is sharp and coherent, with realistic skin texture and lighting that matches the scene.'], inputs={"clip": (clip, 0)}, pos=(-300, 60), |
| title="Prompt (generic anatomy default β edit or clear)") |
| guid = g.add("FluxGuidance", [10.0], inputs={"conditioning": (txt, 0)}, pos=(140, 60)) |
| neg = g.add("CLIPTextEncode", ['deformed anatomy, extra limbs, missing limbs, fused or extra fingers, malformed hands, twisted joints, distorted face, mutated body, disfigured, blurry, lowres, jpeg artifacts, watermark, text, logo, oversaturated, plastic skin'], inputs={"clip": (clip, 0)}, pos=(-300, 250), |
| title="Negative (INERT at CFG 1.0 β activates only if CFG > 1)") |
| apply_style = g.add("StyleModelApply", [0.35, "multiply"], |
| inputs={"conditioning": (guid, 0), "style_model": (sm_loader, 0), |
| "clip_vision_output": (cv_enc, 0)}, pos=(140, 300)) |
| ci, rs, depth = comp_prepass(g, -720, 1000, "flux-v2/style-comp-bfl") |
| ip2p = g.add("InstructPixToPixConditioning", |
| inputs={"positive": (apply_style, 0), "negative": (neg, 0), "vae": (vae, 0), |
| "pixels": (depth, 0)}, pos=(700, 300)) |
| dlora = g.add("LoraLoaderModelOnly", ["flux1-depth-dev-lora.safetensors", 0.85], |
| inputs={"model": (unet, 0)}, pos=(-300, -160), title="BFL Depth LoRA") |
| msf = g.add("ModelSamplingFlux", [1.15, 0.5, W, H], inputs={"model": (dlora, 0)}, pos=(140, -160)) |
| ks = g.add("KSampler", [0, "randomize", 32, 1.0, "euler", "simple", 1.0], |
| inputs={"model": (msf, 0), "positive": (ip2p, 0), "negative": (ip2p, 1), |
| "latent_image": (ip2p, 2)}, pos=(1120, 300)) |
| dec = g.add("VAEDecode", inputs={"samples": (ks, 0), "vae": (vae, 0)}, pos=(1460, 300)) |
| g.add("SaveImage", ["flux-v2/style-comp-bfl"], inputs={"images": (dec, 0)}, pos=(1460, 420)) |
| g.group("STYLE REFERENCE β Redux", -740, 340, 1220, 700, "#3f789e") |
| g.group("COMPOSITION β 896x1152 pre-pass + BFL Depth LoRA", -740, 900, 1500, 620, "#8f5b34") |
| g.dump(f"{B.OUT}/flux-v2-redux-style-composition-bfl-lora.json") |
|
|
|
|
| build_v2_style_composition() |
| build_v2_bfl_lora() |
|
|