setStyle('medium', v)}
- placeholder="Photograph."
+ label={photoBranch ? 'Photo (camera / film)' : 'Art style (rendering technique)'}
+ value={renderValue}
+ onChange={setRender}
+ placeholder={photoBranch ? '35mm film still, shallow depth of field' : 'flat vector, clean edges'}
/>
Color palette (max 16)
diff --git a/ui/src/helpers/defaultSamples.ts b/ui/src/helpers/defaultSamples.ts
index ac197b2..9b2e9f4 100644
--- a/ui/src/helpers/defaultSamples.ts
+++ b/ui/src/helpers/defaultSamples.ts
@@ -417,7 +417,7 @@ export const defaultIdeogramSamplesConfig: SampleConfig = {
"aesthetics": "Cinematic, tense, candid realism.",
"lighting": "Overcast afternoon daylight, soft and low-contrast, cool-neutral white balance.",
"photo": "35mm film still, subtle grain, natural depth of field.",
- "medium": "Photograph.",
+ "medium": "photograph",
"color_palette": ["#8C9B82", "#B7402A", "#5A5F57", "#9AA7AE", "#D98A3D"]
},
"compositional_deconstruction": {
@@ -466,7 +466,7 @@ export const defaultIdeogramSamplesConfig: SampleConfig = {
"aesthetics": "Cozy, relaxed, intimate.",
"lighting": "Soft diffused window daylight, cool-neutral white balance, low contrast.",
"photo": "35mm film still, shallow depth of field, subtle grain.",
- "medium": "Photograph.",
+ "medium": "photograph",
"color_palette": ["#9A5A3E", "#E0D2BA", "#7C7872", "#B07C45", "#33312D"]
},
"compositional_deconstruction": {
@@ -536,7 +536,7 @@ export const defaultIdeogramSamplesConfig: SampleConfig = {
"aesthetics": "High-energy, surreal, neon nightlife.",
"lighting": "Dim club lighting with magenta and cyan washes and crisscrossing green and magenta laser beams cutting through haze.",
"photo": "Fish-eye lens with strong barrel distortion, deep shadow contrast.",
- "medium": "Photograph.",
+ "medium": "photograph",
"color_palette": ["#0B0B12", "#D81E8F", "#1FB6C9", "#37C46A", "#6A4A2E"]
},
"compositional_deconstruction": {
@@ -596,7 +596,7 @@ export const defaultIdeogramSamplesConfig: SampleConfig = {
"aesthetics": "Bright, playful, candid.",
"lighting": "Bright overcast daylight, soft and shadowless, cool-neutral white balance.",
"photo": "35mm film still, natural depth of field, subtle grain.",
- "medium": "Photograph.",
+ "medium": "photograph",
"color_palette": ["#C9B68C", "#2E6B7A", "#9FB7BE", "#1B3A5C", "#E7E2D6"]
},
"compositional_deconstruction": {
@@ -646,7 +646,7 @@ export const defaultIdeogramSamplesConfig: SampleConfig = {
"aesthetics": "Serene, rugged, wintry.",
"lighting": "Pale overcast winter daylight, even and shadowless, cool-neutral white balance.",
"photo": "35mm film still, subtle grain, soft natural focus.",
- "medium": "Photograph.",
+ "medium": "photograph",
"color_palette": ["#E8EDF0", "#6B4A30", "#3C5240", "#9AA6AD", "#C8A877"]
},
"compositional_deconstruction": {
@@ -705,7 +705,7 @@ export const defaultIdeogramSamplesConfig: SampleConfig = {
"aesthetics": "Gritty, energetic, high-contrast.",
"lighting": "Dark stage lit by green and magenta laser beams through haze, deep shadow contrast, cool-neutral white balance.",
"photo": "35mm concert photograph, subtle grain, deep contrast.",
- "medium": "Photograph.",
+ "medium": "photograph",
"color_palette": ["#0C0C10", "#37C46A", "#D81E8F", "#C9C9C9", "#5A4633"]
},
"compositional_deconstruction": {
@@ -775,7 +775,7 @@ export const defaultIdeogramSamplesConfig: SampleConfig = {
"aesthetics": "Rustic, focused, artisanal.",
"lighting": "Diffused overcast daylight from a high window, cool-neutral white balance, low contrast.",
"photo": "35mm film still, subtle grain, natural depth of field.",
- "medium": "Photograph.",
+ "medium": "photograph",
"color_palette": ["#8A5A3C", "#6E4327", "#9A9488", "#4A5340", "#C7B299"]
},
"compositional_deconstruction": {
@@ -845,7 +845,7 @@ export const defaultIdeogramSamplesConfig: SampleConfig = {
"aesthetics": "Clean, minimal, editorial.",
"lighting": "Even diffused studio softbox lighting, neutral white balance, shadowless.",
"photo": "Studio fashion photograph, sharp focus, seamless white cyclorama.",
- "medium": "Photograph.",
+ "medium": "photograph",
"color_palette": ["#F2F2F0", "#9A9CA0", "#2A2F3C", "#5B5E66", "#D8D8D6"]
},
"compositional_deconstruction": {
@@ -874,7 +874,7 @@ export const defaultIdeogramSamplesConfig: SampleConfig = {
"aesthetics": "Plain, candid, documentary.",
"lighting": "Overcast daylight, soft and even, cool-neutral white balance.",
"photo": "35mm film still, eye-level, subtle grain.",
- "medium": "Photograph.",
+ "medium": "photograph",
"color_palette": ["#9AA0A4", "#7C4A38", "#3A3D44", "#1E3A66", "#E8E6E0"]
},
"compositional_deconstruction": {
@@ -924,7 +924,7 @@ export const defaultIdeogramSamplesConfig: SampleConfig = {
"aesthetics": "Rugged, cinematic, post-apocalyptic.",
"lighting": "Pale dust-choked daylight softened by airborne grit, cool-neutral white balance, low contrast.",
"photo": "35mm film still, subtle grain, hazy distance.",
- "medium": "Photograph.",
+ "medium": "photograph",
"color_palette": ["#C2A878", "#6B4A2E", "#3A352E", "#9A8A6C", "#B5562A"]
},
"compositional_deconstruction": {
diff --git a/ui_scripts/upsample_ideogram4_caption.py b/ui_scripts/upsample_ideogram4_caption.py
index 7f60188..1ccc850 100644
--- a/ui_scripts/upsample_ideogram4_caption.py
+++ b/ui_scripts/upsample_ideogram4_caption.py
@@ -21,6 +21,8 @@ REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if REPO_ROOT not in sys.path:
sys.path.insert(0, REPO_ROOT)
+from toolkit.ideogram_caption import normalize_caption_dict
+
# The generation prompt lives here. It's a `name = """"""` file, but the
# content intentionally contains literal `\uNNNN` and `\n` sequences that are not
# valid Python escapes, so it cannot be imported -- we read the triple-quoted
@@ -127,62 +129,22 @@ def sanitize_bbox(bbox):
return [y1, x1, y2, x2]
-HEX_COLOR_RE = re.compile(r"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$")
-
-
-def sanitize_palette(palette, max_len):
- """Keep unique, valid hex colors in order, capped to max_len. Returns the
- cleaned list, or None if nothing valid remains (drop the key)."""
- if not isinstance(palette, (list, tuple)):
- return None
- seen = set()
- out = []
- for c in palette:
- if not isinstance(c, str):
- continue
- c = c.strip()
- if not HEX_COLOR_RE.match(c):
- continue
- key = c.lower()
- if key in seen:
- continue
- seen.add(key)
- out.append(c)
- if len(out) >= max_len:
- break
- return out or None
-
-
def sanitize_caption(data: dict) -> dict:
- """Light cleanup: drop any aspect_ratio key (input-only context, not output),
- clean each bbox, and cap color palettes (16 per image, 5 per element)."""
- data.pop("aspect_ratio", None)
- style = data.get("style_description")
- if isinstance(style, dict) and "color_palette" in style:
- pal = sanitize_palette(style["color_palette"], 16)
- if pal is None:
- style.pop("color_palette", None)
- else:
- style["color_palette"] = pal
+ """Clamp each bbox to valid 0-1000 [y1,x1,y2,x2], then hand off to the shared
+ normalizer for the rest: drop aspect_ratio, enforce the photo/art_style branch
+ and key order, canonicalize medium, and cap/uppercase color palettes (16 per
+ image, 5 per element)."""
decon = data.get("compositional_deconstruction", {})
- elements = decon.get("elements", [])
+ elements = decon.get("elements", []) if isinstance(decon, dict) else []
if isinstance(elements, list):
for el in elements:
- if not isinstance(el, dict):
- continue
- if "bbox" in el:
+ if isinstance(el, dict) and "bbox" in el:
cleaned = sanitize_bbox(el["bbox"])
if cleaned is None:
el.pop("bbox", None)
else:
el["bbox"] = cleaned
- if "color_palette" in el:
- pal = sanitize_palette(el["color_palette"], 5)
- if pal is None:
- el.pop("color_palette", None)
- else:
- el["color_palette"] = pal
- return data
+ return normalize_caption_dict(data)
def upsample_one(
diff --git a/version.py b/version.py
index 4c65788..46ee4ed 100644
--- a/version.py
+++ b/version.py
@@ -1 +1 @@
-VERSION = "0.10.11"
+VERSION = "0.10.12"