Add SDXL bucket size node
This commit is contained in:
@@ -9,6 +9,7 @@ The node is registered as:
|
||||
- `prompt_builder / SxCP Global Seed`
|
||||
- `prompt_builder / SxCP Seed Control`
|
||||
- `prompt_builder / SxCP Seed Locker`
|
||||
- `prompt_builder / util / SxCP SDXL Bucket Size`
|
||||
- `prompt_builder / SxCP Camera Control`
|
||||
- `prompt_builder / SxCP Camera Orbit Control`
|
||||
- `prompt_builder / SxCP Qwen Camera Translator`
|
||||
@@ -309,6 +310,13 @@ you like, choose one `reroll_axis`, and connect its `seed_config`. All other
|
||||
axes stay frozen to `base_seed`; the rerolled axis follows `reroll_seed`, or the
|
||||
main prompt seed when `reroll_seed=-1`.
|
||||
|
||||
`SxCP SDXL Bucket Size` randomly selects one of the built-in SDXL bucket
|
||||
resolutions and outputs `width`, `height`, and a `resolution` string. Use
|
||||
`orientation` to restrict the pool to portrait, square, or landscape. Leave
|
||||
`bucket_index=0` for random selection, or set `bucket_index` to pick a specific
|
||||
position inside the filtered pool. Connect `SxCP Global Seed` or Seed Locker's
|
||||
`seed_config` when the bucket choice must be reproducible.
|
||||
|
||||
`SxCP Camera Control` and `SxCP Camera Orbit Control` output `camera_config`,
|
||||
which can be connected to the prompt builder or the Insta/OF pair node. They
|
||||
make camera/framing first-class instead of relying on a weak phrase inside the
|
||||
|
||||
+123
@@ -27,6 +27,20 @@ SXCP_CHARACTER_CAST = "SXCP_CHARACTER_CAST"
|
||||
SXCP_CHARACTER_SLOT = "SXCP_CHARACTER_SLOT"
|
||||
SXCP_CHARACTER_PROFILE = "SXCP_CHARACTER_PROFILE"
|
||||
|
||||
SDXL_BUCKET_RESOLUTIONS = [
|
||||
{"orientation": "portrait", "width": 896, "height": 1792, "aspect": 0.50, "mp": 1.61},
|
||||
{"orientation": "portrait", "width": 960, "height": 1664, "aspect": 0.58, "mp": 1.60},
|
||||
{"orientation": "portrait", "width": 1024, "height": 1600, "aspect": 0.64, "mp": 1.64},
|
||||
{"orientation": "portrait", "width": 1088, "height": 1472, "aspect": 0.74, "mp": 1.60},
|
||||
{"orientation": "portrait", "width": 1152, "height": 1408, "aspect": 0.82, "mp": 1.62},
|
||||
{"orientation": "portrait", "width": 1216, "height": 1344, "aspect": 0.90, "mp": 1.63},
|
||||
{"orientation": "square", "width": 1280, "height": 1280, "aspect": 1.00, "mp": 1.64},
|
||||
{"orientation": "landscape", "width": 1344, "height": 1216, "aspect": 1.11, "mp": 1.63},
|
||||
{"orientation": "landscape", "width": 1408, "height": 1152, "aspect": 1.22, "mp": 1.62},
|
||||
{"orientation": "landscape", "width": 1472, "height": 1088, "aspect": 1.35, "mp": 1.60},
|
||||
{"orientation": "landscape", "width": 1536, "height": 1024, "aspect": 1.50, "mp": 1.57},
|
||||
]
|
||||
|
||||
|
||||
COMMON_INPUT_TOOLTIPS = {
|
||||
"row_number": "Generation row to use. Changing it advances the deterministic selection without changing the main seed.",
|
||||
@@ -63,6 +77,8 @@ COMMON_INPUT_TOOLTIPS = {
|
||||
"extra_negative": "Extra negative text appended after the generated negative prompt.",
|
||||
"trigger": "Training or style trigger token.",
|
||||
"prepend_trigger_to_prompt": "If enabled, put the trigger token at the start of generated prompts.",
|
||||
"bucket_index": "0 picks a random bucket. 1+ picks that position inside the selected orientation pool.",
|
||||
"megapixels": "Approximate megapixel count for the selected bucket.",
|
||||
"enabled": "Enable this node's effect while keeping it wired in the graph.",
|
||||
"combine_mode": "replace starts a new pool/config; add merges selected values into the incoming config.",
|
||||
"manual": "Manual character details config. Non-empty manual fields override generated slot details.",
|
||||
@@ -187,6 +203,13 @@ NODE_INPUT_TOOLTIPS = {
|
||||
"SxCPSeedLocker": {
|
||||
"reroll_axis": "Choose the one axis to change while the rest stays locked. Use pose for sexual pose, scene for location, person for appearance.",
|
||||
},
|
||||
"SxCPSDXLBucketSize": {
|
||||
"orientation": "Bucket orientation filter. any uses the full table; portrait/square/landscape restrict random selection.",
|
||||
"seed": "Fixed bucket seed. Use -1 for a fresh random bucket each queue, or connect Global Seed for reproducible sizes.",
|
||||
"row_number": "Deterministic row offset for the bucket. With a fixed seed, changing this advances the bucket choice.",
|
||||
"bucket_index": "0=random. 1+ selects that bucket position inside the selected orientation pool and ignores seed.",
|
||||
"seed_config": "Optional seed config. The composition seed controls bucket choice, so Seed Locker can keep sizes fixed while rerolling pose/person.",
|
||||
},
|
||||
"SxCPCameraControl": {
|
||||
"camera_mode": "Camera style preset. Use from_camera_config in Insta/OF options to consume this.",
|
||||
"priority": "locked makes the camera wording strict; soft_hint allows the model more freedom.",
|
||||
@@ -817,6 +840,104 @@ class SxCPSeedLocker:
|
||||
return config, summary
|
||||
|
||||
|
||||
class SxCPSDXLBucketSize:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"orientation": (["any", "portrait", "square", "landscape"], {"default": "any"}),
|
||||
"seed": ("INT", {"default": -1, "min": -1, "max": 0xFFFFFFFF, "step": 1}),
|
||||
"row_number": ("INT", {"default": 1, "min": 1, "max": 1000000, "step": 1}),
|
||||
"bucket_index": ("INT", {"default": 0, "min": 0, "max": len(SDXL_BUCKET_RESOLUTIONS), "step": 1}),
|
||||
},
|
||||
"optional": {
|
||||
"seed_config": (SXCP_SEED_CONFIG,),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT", "STRING", "STRING", "FLOAT", "FLOAT", "INT", "STRING")
|
||||
RETURN_NAMES = ("width", "height", "resolution", "orientation", "aspect", "megapixels", "bucket_index", "summary")
|
||||
FUNCTION = "build"
|
||||
CATEGORY = "prompt_builder/util"
|
||||
|
||||
@staticmethod
|
||||
def _configured_bucket_seed(seed_config):
|
||||
if not seed_config:
|
||||
return None
|
||||
if isinstance(seed_config, dict):
|
||||
raw = seed_config
|
||||
else:
|
||||
try:
|
||||
raw = json.loads(str(seed_config))
|
||||
except (TypeError, ValueError, json.JSONDecodeError):
|
||||
return None
|
||||
if not isinstance(raw, dict):
|
||||
return None
|
||||
for key in ("composition_seed", "content_seed", "seed", "global_seed"):
|
||||
try:
|
||||
value = int(raw.get(key))
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if value >= 0:
|
||||
return value
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *args, **kwargs):
|
||||
seed_value = kwargs.get("seed")
|
||||
if seed_value is None and len(args) > 1:
|
||||
seed_value = args[1]
|
||||
bucket_index = kwargs.get("bucket_index")
|
||||
if bucket_index is None and len(args) > 3:
|
||||
bucket_index = args[3]
|
||||
seed_config = kwargs.get("seed_config", "")
|
||||
if not seed_config and len(args) > 4:
|
||||
seed_config = args[4]
|
||||
try:
|
||||
seed = int(seed_value)
|
||||
except (TypeError, ValueError):
|
||||
seed = -1
|
||||
try:
|
||||
index = int(bucket_index)
|
||||
except (TypeError, ValueError):
|
||||
index = 0
|
||||
if index <= 0 and seed < 0 and cls._configured_bucket_seed(seed_config) is None:
|
||||
return random.random()
|
||||
return tuple(args), tuple(sorted(kwargs.items()))
|
||||
|
||||
def build(self, orientation, seed, row_number, bucket_index, seed_config=""):
|
||||
orientation = str(orientation or "any").strip().lower()
|
||||
pool = [
|
||||
(index + 1, bucket)
|
||||
for index, bucket in enumerate(SDXL_BUCKET_RESOLUTIONS)
|
||||
if orientation == "any" or bucket["orientation"] == orientation
|
||||
]
|
||||
if not pool:
|
||||
pool = list(enumerate(SDXL_BUCKET_RESOLUTIONS, start=1))
|
||||
if int(bucket_index) > 0:
|
||||
pool_position = max(1, min(len(pool), int(bucket_index))) - 1
|
||||
else:
|
||||
configured_seed = self._configured_bucket_seed(seed_config)
|
||||
if configured_seed is None and int(seed) < 0:
|
||||
rng = random.Random(random.getrandbits(64))
|
||||
else:
|
||||
bucket_seed = configured_seed if configured_seed is not None else int(seed)
|
||||
rng = random.Random(f"sdxl_bucket:{bucket_seed}:{int(row_number)}:{orientation}")
|
||||
pool_position = rng.randrange(len(pool))
|
||||
selected_index, selected = pool[pool_position]
|
||||
width = int(selected["width"])
|
||||
height = int(selected["height"])
|
||||
selected_orientation = str(selected["orientation"])
|
||||
aspect = float(selected["aspect"])
|
||||
mp = float(selected["mp"])
|
||||
resolution = f"{width}x{height}"
|
||||
summary = (
|
||||
f"{selected_orientation} bucket {pool_position + 1}/{len(pool)} "
|
||||
f"(table {selected_index}): {resolution}, aspect {aspect:.2f}, {mp:.2f} MP"
|
||||
)
|
||||
return width, height, resolution, selected_orientation, aspect, mp, selected_index, summary
|
||||
|
||||
|
||||
class SxCPCameraControl:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -2475,6 +2596,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"SxCPGlobalSeed": SxCPGlobalSeed,
|
||||
"SxCPSeedControl": SxCPSeedControl,
|
||||
"SxCPSeedLocker": SxCPSeedLocker,
|
||||
"SxCPSDXLBucketSize": SxCPSDXLBucketSize,
|
||||
"SxCPCameraControl": SxCPCameraControl,
|
||||
"SxCPCameraOrbitControl": SxCPCameraOrbitControl,
|
||||
"SxCPQwenCameraTranslator": SxCPQwenCameraTranslator,
|
||||
@@ -2515,6 +2637,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SxCPGlobalSeed": "SxCP Global Seed",
|
||||
"SxCPSeedControl": "SxCP Seed Control",
|
||||
"SxCPSeedLocker": "SxCP Seed Locker",
|
||||
"SxCPSDXLBucketSize": "SxCP SDXL Bucket Size",
|
||||
"SxCPCameraControl": "SxCP Camera Control",
|
||||
"SxCPCameraOrbitControl": "SxCP Camera Orbit Control",
|
||||
"SxCPQwenCameraTranslator": "SxCP Qwen Camera Translator",
|
||||
|
||||
Reference in New Issue
Block a user