Added support for watermark, and regionalPrompt

This commit is contained in:
Sirshendu Ganguly
2026-03-12 14:21:38 -04:00
parent c17d46d70b
commit aa5f547b99
13 changed files with 1219 additions and 616 deletions
+1 -1
View File
@@ -195,7 +195,7 @@ class txt2img:
"tooltip": "Connect Runware Image Inference Ultralytics node to configure Ultralytics parameters (maskBlur, maskPadding, confidence, prompts, steps, CFGScale, strength).",
}),
"advancedFeatures": ("RUNWAREIMAGEINFERENCEADVANCEDFEATURES", {
"tooltip": "Connect Runware Image Inference Advanced Features for layerDiffuse (transparency) and hiresFix (two-stage high-res).",
"tooltip": "Connect Runware Image Advanced Feature Input for layerDiffuse (transparency) and hiresFix (two-stage high-res).",
}),
}
}
+80 -69
View File
@@ -1,69 +1,80 @@
"""
Runware Image Inference Advanced Features Node
Provides advancedFeatures (layerDiffuse, hiresFix) for Runware Image Inference.
"""
from typing import Dict, Any
class RunwareImageInferenceAdvancedFeatures:
"""Runware Image Inference Advanced Features Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useLayerDiffuse": ("BOOLEAN", {
"tooltip": "Enable to include layerDiffuse in advancedFeatures. When enabled, LayerDiffuse generates images with transparency (alpha) without post-processing background removal.",
"default": False,
}),
"layerDiffuse": ("BOOLEAN", {
"tooltip": "Enable LayerDiffuse technology for direct generation of images with transparency (alpha channels). Only used when 'Use Layer Diffuse' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useHiresFix": ("BOOLEAN", {
"tooltip": "Enable to include hiresFix in advancedFeatures. When enabled, uses a two-stage process for higher-resolution, higher-detail images.",
"default": False,
}),
"hiresFix": ("BOOLEAN", {
"tooltip": "Enable a two-stage generation: first at native resolution, then upscale and refine in a second pass. Only used when 'Use Hires Fix' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
}
}
RETURN_TYPES = ("RUNWAREIMAGEINFERENCEADVANCEDFEATURES",)
RETURN_NAMES = ("advancedFeatures",)
FUNCTION = "createAdvancedFeatures"
CATEGORY = "Runware"
DESCRIPTION = "Configure advanced features for Runware Image Inference (layerDiffuse for transparency, hiresFix for two-stage high-res generation). Connect to Runware Image Inference advancedFeatures input."
def createAdvancedFeatures(self, **kwargs) -> tuple:
"""Build advancedFeatures dict for the API."""
use_layer_diffuse = kwargs.get("useLayerDiffuse", False)
layer_diffuse = kwargs.get("layerDiffuse", False)
use_hires_fix = kwargs.get("useHiresFix", False)
hires_fix = kwargs.get("hiresFix", False)
advanced_features: Dict[str, Any] = {}
if use_layer_diffuse:
advanced_features["layerDiffuse"] = bool(layer_diffuse)
if use_hires_fix:
advanced_features["hiresFix"] = bool(hires_fix)
return (advanced_features,)
NODE_CLASS_MAPPINGS = {
"RunwareImageInferenceAdvancedFeatures": RunwareImageInferenceAdvancedFeatures,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareImageInferenceAdvancedFeatures": "Runware Image Inference Advanced Features",
}
from typing import Dict, Any
class RunwareImageInferenceAdvancedFeatures:
"""Runware Image Advanced Feature Input Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useLayerDiffuse": ("BOOLEAN", {
"tooltip": "Enable to include layerDiffuse in advancedFeatures. When enabled, LayerDiffuse generates images with transparency (alpha) without post-processing background removal.",
"default": False,
}),
"layerDiffuse": ("BOOLEAN", {
"tooltip": "Enable LayerDiffuse technology for direct generation of images with transparency (alpha channels). Only used when 'Use Layer Diffuse' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useHiresFix": ("BOOLEAN", {
"tooltip": "Enable to include hiresFix in advancedFeatures. When enabled, uses a two-stage process for higher-resolution, higher-detail images.",
"default": False,
}),
"hiresFix": ("BOOLEAN", {
"tooltip": "Enable a two-stage generation: first at native resolution, then upscale and refine in a second pass. Only used when 'Use Hires Fix' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"watermark": ("RUNWAREIMAGEWATERMARKADVFEATURE", {
"tooltip": "Connect Runware Watermark Advanced Feature to include watermark configuration in advancedFeatures.watermark.",
}),
"regionalPrompting": ("RUNWAREREGIONALPROMPTINGADVFEATURE", {
"tooltip": "Connect Runware Regional Prompting Advanced Feature to configure regionalPrompting in advancedFeatures.",
}),
},
}
RETURN_TYPES = ("RUNWAREIMAGEINFERENCEADVANCEDFEATURES",)
RETURN_NAMES = ("advancedFeatures",)
FUNCTION = "createAdvancedFeatures"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure advanced features for Runware Image Inference: layerDiffuse (transparency), "
"hiresFix (two-stage high-res), watermark, and regional prompting. "
"Connect to Runware Image Inference advancedFeatures input."
)
def createAdvancedFeatures(self, watermark=None, regionalPrompting=None, **kwargs) -> tuple:
"""Build advancedFeatures dict for the API."""
use_layer_diffuse = kwargs.get("useLayerDiffuse", False)
layer_diffuse = kwargs.get("layerDiffuse", False)
use_hires_fix = kwargs.get("useHiresFix", False)
hires_fix = kwargs.get("hiresFix", False)
advanced_features: Dict[str, Any] = {}
if use_layer_diffuse:
advanced_features["layerDiffuse"] = bool(layer_diffuse)
if use_hires_fix:
advanced_features["hiresFix"] = bool(hires_fix)
if isinstance(watermark, dict) and watermark:
advanced_features["watermark"] = watermark
if isinstance(regionalPrompting, dict) and regionalPrompting:
advanced_features["regionalPrompting"] = regionalPrompting
return (advanced_features,)
NODE_CLASS_MAPPINGS = {
"RunwareImageInferenceAdvancedFeatures": RunwareImageInferenceAdvancedFeatures,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareImageInferenceAdvancedFeatures": "Runware Image Advanced Feature Input",
}
@@ -0,0 +1,80 @@
from typing import Dict, Any, Tuple
class RunwareRegionalPromptingAdvancedFeature:
"""
Runware Regional Prompting Advanced Feature Node
Builds the advancedFeatures.regionalPrompting configuration object:
- injectSteps
- backgroundPrompt
- baseRatio
- regions (connected from the Regions node)
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"injectSteps": ("INT", {
"tooltip": "Steps to inject regional masks (1-100).",
"default": 15,
"min": 1,
"max": 100,
}),
"backgroundPrompt": ("STRING", {
"tooltip": "Background prompt applied to uncovered areas.",
"default": "",
}),
"baseRatio": ("FLOAT", {
"tooltip": "Base prompt attention weight (0.0 - 1.0).",
"default": 0.0,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
"regions": ("RUNWAREREGIONALPROMPTINGREGIONS", {
"tooltip": "Connect Runware Regional Prompting Advanced Feature Regions node to define regional prompts and masks.",
}),
},
}
RETURN_TYPES: Tuple[str] = ("RUNWAREREGIONALPROMPTINGADVFEATURE",)
RETURN_NAMES = ("regionalPrompting",)
FUNCTION = "createRegionalPrompting"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure regional prompting advanced feature (injectSteps, backgroundPrompt, "
"baseRatio, and regions). Connect output to Runware Image Advanced Feature Input "
"regionalPrompting input."
)
def createRegionalPrompting(self, regions=None, **kwargs) -> tuple:
"""Build regionalPrompting dict for advancedFeatures.regionalPrompting."""
inject_steps = int(kwargs.get("injectSteps", 15))
base_ratio = float(kwargs.get("baseRatio", 0.2))
background_prompt = (kwargs.get("backgroundPrompt", "") or "").strip()
config: Dict[str, Any] = {
"injectSteps": inject_steps,
"baseRatio": base_ratio,
}
if background_prompt:
config["backgroundPrompt"] = background_prompt
if isinstance(regions, list) and regions:
config["regions"] = regions
return (config,)
NODE_CLASS_MAPPINGS = {
"RunwareRegionalPromptingAdvancedFeature": RunwareRegionalPromptingAdvancedFeature,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareRegionalPromptingAdvancedFeature": "Runware Regional Prompting Advanced Feature",
}
@@ -0,0 +1,196 @@
from typing import Dict, Any, List, Tuple
class RunwareRegionalPromptingAdvancedFeatureRegions:
"""
Runware Regional Prompting Advanced Feature Regions Node
Builds the advancedFeatures.regionalPrompting.regions array, with up to 4 regions.
Each region has a prompt and a rectangular mask [x0, y0, x1, y1].
"""
@classmethod
def INPUT_TYPES(cls):
coords_tooltip = "Mask rectangle as [x0, y0, x1, y1] in image pixel coordinates."
return {
"required": {},
"optional": {
# Region 1
"useRegion1": ("BOOLEAN", {
"tooltip": "Enable first regional prompt mask.",
"default": False,
}),
"region1Prompt": ("STRING", {
"tooltip": "Prompt for region 1.",
"default": "",
}),
"region1_x0": ("INT", {
"tooltip": coords_tooltip,
"default": 0,
"min": 0,
"max": 8192,
}),
"region1_y0": ("INT", {
"tooltip": coords_tooltip,
"default": 0,
"min": 0,
"max": 8192,
}),
"region1_x1": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region1_y1": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
# Region 2
"useRegion2": ("BOOLEAN", {
"tooltip": "Enable second regional prompt mask.",
"default": False,
}),
"region2Prompt": ("STRING", {
"tooltip": "Prompt for region 2.",
"default": "",
}),
"region2_x0": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region2_y0": ("INT", {
"tooltip": coords_tooltip,
"default": 0,
"min": 0,
"max": 8192,
}),
"region2_x1": ("INT", {
"tooltip": coords_tooltip,
"default": 1024,
"min": 0,
"max": 8192,
}),
"region2_y1": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
# Region 3
"useRegion3": ("BOOLEAN", {
"tooltip": "Enable third regional prompt mask.",
"default": False,
}),
"region3Prompt": ("STRING", {
"tooltip": "Prompt for region 3.",
"default": "",
}),
"region3_x0": ("INT", {
"tooltip": coords_tooltip,
"default": 0,
"min": 0,
"max": 8192,
}),
"region3_y0": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region3_x1": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region3_y1": ("INT", {
"tooltip": coords_tooltip,
"default": 1024,
"min": 0,
"max": 8192,
}),
# Region 4
"useRegion4": ("BOOLEAN", {
"tooltip": "Enable fourth regional prompt mask.",
"default": False,
}),
"region4Prompt": ("STRING", {
"tooltip": "Prompt for region 4.",
"default": "",
}),
"region4_x0": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region4_y0": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region4_x1": ("INT", {
"tooltip": coords_tooltip,
"default": 1024,
"min": 0,
"max": 8192,
}),
"region4_y1": ("INT", {
"tooltip": coords_tooltip,
"default": 1024,
"min": 0,
"max": 8192,
}),
},
}
RETURN_TYPES: Tuple[str] = ("RUNWAREREGIONALPROMPTINGREGIONS",)
RETURN_NAMES = ("regions",)
FUNCTION = "createRegions"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure regions array for regional prompting. Each enabled region provides "
"a prompt and a rectangular mask [x0, y0, x1, y1]. Connect to Runware Regional "
"Prompting Advanced Feature regions input."
)
def createRegions(self, **kwargs) -> tuple:
"""Build list of regional prompting regions."""
regions: List[Dict[str, Any]] = []
for idx in range(1, 5):
use_region = kwargs.get(f"useRegion{idx}", False)
prompt = (kwargs.get(f"region{idx}Prompt", "") or "").strip()
if not use_region or not prompt:
continue
x0 = int(kwargs.get(f"region{idx}_x0", 0))
y0 = int(kwargs.get(f"region{idx}_y0", 0))
x1 = int(kwargs.get(f"region{idx}_x1", 0))
y1 = int(kwargs.get(f"region{idx}_y1", 0))
mask = [x0, y0, x1, y1]
regions.append(
{
"prompt": prompt,
"mask": mask,
}
)
return (regions,)
NODE_CLASS_MAPPINGS = {
"RunwareRegionalPromptingAdvancedFeatureRegions": RunwareRegionalPromptingAdvancedFeatureRegions,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareRegionalPromptingAdvancedFeatureRegions": "Runware Regional Prompting Advanced Feature Regions",
}
+154
View File
@@ -0,0 +1,154 @@
from typing import Dict, Any, Tuple
from .utils.runwareUtils import convertTensor2IMG
class RunwareWatermarkAdvancedFeature:
"""Runware Watermark Advanced Feature Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useText": ("BOOLEAN", {
"tooltip": "Enable to set text content for the watermark (2-32 characters).",
"default": False,
}),
"text": ("STRING", {
"tooltip": "Text content for the watermark (2-32 characters). Only used when 'Use Text' is enabled.",
"default": "",
}),
"useImage": ("BOOLEAN", {
"tooltip": "Enable to set image content for the watermark (connect an IMAGE, UUID, URL, data URI, or base64).",
"default": False,
}),
"image": ("IMAGE", {
"tooltip": "Image to use as watermark. Can be connected from a Load Image node; internally converted to a data URI/UUID when used.",
}),
"useDisplayPosition": ("BOOLEAN", {
"tooltip": "Enable to set watermark display position.",
"default": False,
}),
"displayPosition": ([
"top-left", "top-center", "top-right",
"center-left", "center-center", "center-right",
"bottom-left", "bottom-center", "bottom-right",
], {
"tooltip": "Watermark position on the image.",
"default": "bottom-right",
}),
"useTiled": ("BOOLEAN", {
"tooltip": "Enable to tile the watermark across the image at -45 degrees.",
"default": False,
}),
"tiled": ("BOOLEAN", {
"tooltip": "When enabled, distribute watermark across the image at -45 degrees. Only used when 'Use Tiled' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useOpacity": ("BOOLEAN", {
"tooltip": "Enable to set watermark opacity (0.1 - 1.0).",
"default": False,
}),
"opacity": ("FLOAT", {
"tooltip": "Watermark opacity (0.1 - 1.0). Only used when 'Use Opacity' is enabled.",
"default": 0.6,
"min": 0.1,
"max": 1.0,
"step": 0.05,
}),
"useFontColor": ("BOOLEAN", {
"tooltip": "Enable to set watermark font color (hex).",
"default": False,
}),
"fontColor": ("STRING", {
"tooltip": "Text color in hex format (e.g. #ffffff). Only used when 'Use Font Color' is enabled.",
"default": "#ffffff",
}),
"useBgColor": ("BOOLEAN", {
"tooltip": "Enable to set watermark background color (hex).",
"default": False,
}),
"bgColor": ("STRING", {
"tooltip": "Background color in hex format (e.g. #000000 or transparent). Only used when 'Use Bg Color' is enabled.",
"default": "transparent",
}),
},
}
RETURN_TYPES: Tuple[str] = ("RUNWAREIMAGEWATERMARKADVFEATURE",)
RETURN_NAMES = ("watermark",)
FUNCTION = "createWatermark"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure watermark advanced feature for Runware Image Inference. "
"You must provide exactly one of text or image. Connect output to "
"Runware Image Advanced Feature Input watermark connection."
)
def createWatermark(self, **kwargs) -> tuple:
"""Build watermark dict for advancedFeatures.watermark."""
use_text = kwargs.get("useText", False)
text = (kwargs.get("text", "") or "").strip()
use_image = kwargs.get("useImage", False)
raw_image = kwargs.get("image", None)
has_text = use_text and bool(text)
image_str: str = ""
if use_image and raw_image is not None:
# If an IMAGE tensor is connected, convert it to a data URI / UUID.
try:
# convertTensor2IMG handles caching and returns either UUID or data URI.
image_str = convertTensor2IMG(raw_image)
except Exception:
image_str = ""
has_image = use_image and bool(image_str)
if has_text == has_image:
raise Exception(
"Watermark configuration requires exactly one of text or image (but not both or neither)."
)
use_pos = kwargs.get("useDisplayPosition", False)
pos = kwargs.get("displayPosition", "bottom-right")
use_tiled = kwargs.get("useTiled", False)
tiled = bool(kwargs.get("tiled", False))
use_opacity = kwargs.get("useOpacity", False)
opacity = float(kwargs.get("opacity", 0.6))
use_font = kwargs.get("useFontColor", False)
font_color = (kwargs.get("fontColor", "#ffffff") or "").strip()
use_bg = kwargs.get("useBgColor", False)
bg_color = (kwargs.get("bgColor", "transparent") or "").strip()
watermark: Dict[str, Any] = {}
if has_text:
watermark["text"] = text
if has_image:
watermark["image"] = image_str
if use_pos:
watermark["displayPosition"] = pos
if use_tiled:
watermark["tiled"] = tiled
if use_opacity:
watermark["opacity"] = opacity
if use_font and font_color:
watermark["fontColor"] = font_color
if use_bg and bg_color:
watermark["bgColor"] = bg_color
return (watermark,)
NODE_CLASS_MAPPINGS = {
"RunwareWatermarkAdvancedFeature": RunwareWatermarkAdvancedFeature,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareWatermarkAdvancedFeature": "Runware Watermark Advanced Feature",
}