diff --git a/README.md b/README.md index 005e7ec..4dcdda0 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,15 @@ Z-Image-Turbo does not natively consume Ideogram-style bounding-box caption JSON `conditioning_mode`: - `single_prompt_fast`: recommended default. Region prompts are folded into one Z-Image prompt and masks are output separately. This keeps sampling speed close to a normal Z-Image workflow. -- `regional_conditioning_slow`: emits one masked conditioning per region. This can multiply sampler work by the number of regions and may not improve Z-Image-Turbo adherence. +- `regional_conditioning_slow`: experimental for Z-Image-Turbo. It emits one masked conditioning per region, can multiply sampler work by the number of regions, and often does not improve adherence because ZIT is not designed like Ideogram's API regional editor. -For image-to-image edits, connect `image` and `vae`, then use `latent_with_noise_mask` as the sampler latent. The node's `batch_size` repeats a single source latent/mask when you want multiple variations. This is the closest native ComfyUI path for "only change the selected area". Preservation still depends on Z-Image-Turbo, denoise, mask feather, and the workflow; it is not equivalent to Ideogram 4's API-level regional editor. +For text-to-image, region text is treated as additional positive prompt text with a rough area hint. Z-Image-Turbo may still ignore the rectangle or move the concept because it does not natively support hard regional prompt boxes. + +For image-to-image edits, connect `image` and `vae`, then use `latent_with_noise_mask` as the sampler latent. The node's `batch_size` repeats a single source latent/mask when you want multiple variations. In testing, KSampler denoise around `0.6` to `0.8` is usually the practical range for visible regional changes while preserving the rest of the image. This is the closest native ComfyUI path for "only change the selected area". Preservation still depends on Z-Image-Turbo, denoise, mask feather, and the workflow; it is not equivalent to Ideogram 4's API-level regional editor. + +`default_feather` controls mask edge softness in pixels. `0` is a hard rectangle edge, `8-24` is a normal soft edge, and `32+` creates a very broad transition. + +`default_region_strength` controls mask opacity/weight for generated region masks and `latent_with_noise_mask`. In `single_prompt_fast` it does not make the text prompt stronger. For img2img edits, keep it near `1.0` unless you intentionally want a weaker/noisier mask edge effect; use sampler denoise for edit intensity. ## Examples diff --git a/nodes/zimage_turbo_region_builder.py b/nodes/zimage_turbo_region_builder.py index c717bef..13ed3b0 100644 --- a/nodes/zimage_turbo_region_builder.py +++ b/nodes/zimage_turbo_region_builder.py @@ -82,11 +82,16 @@ def _compose_region_prompt(global_prompt, boxes): parts = [(global_prompt or "").strip()] region_parts = [] for i, box in enumerate(boxes, start=1): - prompt = (box.get("prompt") or box.get("desc") or "").strip() + prompt = (box.get("desc") or box.get("prompt") or "").strip() if prompt: - region_parts.append(f"region {i}: {prompt}") + x, y, w, h = _norm_box(box) + cx = x + w / 2 + cy = y + h / 2 + horiz = "left" if cx < 0.34 else "right" if cx > 0.66 else "center" + vert = "upper" if cy < 0.34 else "lower" if cy > 0.66 else "middle" + region_parts.append(f"region {i} ({vert} {horiz} area): {prompt}") if region_parts: - parts.append("; ".join(region_parts)) + parts.append("Regional positive instructions. Apply these details in their specified image areas: " + "; ".join(region_parts)) return "\n".join(p for p in parts if p) @@ -218,11 +223,13 @@ serialized region data for workflow save/load. io.String.Input("global_prompt", multiline=True, default="", dynamic_prompts=True), io.String.Input("global_negative_prompt", multiline=True, default="", dynamic_prompts=True), io.Combo.Input("mode", options=["text_to_image", "image_to_image_region_edit"], default="text_to_image"), - io.Float.Input("default_region_strength", default=1.0, min=0.0, max=10.0, step=0.01), - io.Float.Input("default_feather", default=16.0, min=0.0, max=512.0, step=1.0), + io.Float.Input("default_region_strength", default=1.0, min=0.0, max=10.0, step=0.01, + tooltip="Default mask strength for regions. In single_prompt_fast this affects mask/noise_mask outputs, not text prompt strength. For img2img edits keep near 1.0 and control change with KSampler denoise."), + io.Float.Input("default_feather", default=16.0, min=0.0, max=512.0, step=1.0, + tooltip="Default mask feather in pixels. 0 = hard rectangle edge, 8-24 = normal soft edge, 32+ = very soft transition."), io.Combo.Input("conditioning_mode", options=["single_prompt_fast", "regional_conditioning_slow"], default="single_prompt_fast", - tooltip="single_prompt_fast is recommended for Z-Image-Turbo. regional_conditioning_slow adds one masked conditioning per region and can multiply sampling time."), + tooltip="Recommended: single_prompt_fast. regional_conditioning_slow is experimental for Z-Image-Turbo, often slower and less reliable because ZIT is not designed for masked multi-conditioning."), io.Image.Input("image", optional=True, tooltip="Optional source/reference image shown in the editor and passed through."), io.Vae.Input("vae", optional=True, tooltip="Optional VAE. When provided with an image, the node also outputs an encoded latent with combined_mask as noise_mask for img2img regional edits."), io.String.Input("regions_data", default="", socketless=True, advanced=True), @@ -284,7 +291,7 @@ serialized region data for workflow save/load. masks.append(mask) bbox_dicts.append(bbox) strength = float(box.get("strength", default_region_strength) or default_region_strength) - prompt = (box.get("prompt") or box.get("desc") or "").strip() + prompt = (box.get("desc") or box.get("prompt") or "").strip() neg_prompt = (box.get("negative_prompt") or "").strip() if conditioning_mode == "regional_conditioning_slow" and prompt: positive += _set_mask(_encode_zimage(clip, prompt), mask.unsqueeze(0), strength) @@ -338,6 +345,7 @@ serialized region data for workflow save/load. "conditioning_mode": conditioning_mode, "global_prompt": global_prompt, "global_negative_prompt": global_negative_prompt, + "composed_positive_prompt": _compose_region_prompt(global_prompt, boxes), "regions": region_records, } diff --git a/web/js/zimage_turbo_region_builder.js b/web/js/zimage_turbo_region_builder.js index 426d415..38e1c81 100644 --- a/web/js/zimage_turbo_region_builder.js +++ b/web/js/zimage_turbo_region_builder.js @@ -901,11 +901,11 @@ app.registerExtension({ y: b.y || 0, w: b.w || 0, h: b.h || 0, - prompt: b.prompt || b.desc || "", + prompt: b.desc || b.prompt || "", negative_prompt: b.negative_prompt || "", strength: Number.isFinite(Number(b.strength)) ? Number(b.strength) : 1.0, feather: Number.isFinite(Number(b.feather)) ? Number(b.feather) : 16.0, - desc: b.prompt || b.desc || "", + desc: b.desc || b.prompt || "", })), }, null, 2); }