diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f6c096e..3373d4d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,6 +25,9 @@ jobs:
- name: suppression and invisible-edit rejection
working-directory: backend
run: python test_eval_guard.py
+ - name: branch edits stay local and never add text
+ working-directory: backend
+ run: python test_branches.py
frontend-build:
name: frontend typecheck + build
diff --git a/.gitignore b/.gitignore
index ac46ac2..86b842e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,4 @@ backend/demo_out/
# Static web demo build (npm run build:demo)
web-dist/
+.firebase/
diff --git a/README.md b/README.md
index 6fab522..9ad96de 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,7 @@ It is a small win, and it is the kind Pixel now accepts. An earlier version of t

-This is a recorded optimizer run on the same Red Bull ad, with every edit saved. Each one looks more like a finished campaign than the original, and each one scored lower. Every one of them added a headline or a slogan, and text pulls the eye off the can. So Pixel kept the original and reported a change of zero. The edit that won above added no text at all.
+This is a recorded optimizer run on the same Red Bull ad, with every edit saved. Each one looks more like a finished campaign than the original, and each one scored lower. Every one of them added a headline or a slogan, and text pulls the eye off the can. So Pixel kept the original and reported a change of zero. The edit that won above added no text at all. Those were the old preset branches. The current ones remove the attention thieves one at a time and paste back only what changed. On the Nike billboard, 4 of 5 branches now win (59 to 77). [`docs/PIXEL_FINDINGS.md`](docs/PIXEL_FINDINGS.md) has the measurements behind that change.
The first version of Pixel could not do this. It measured raw attention inside the brand box, so "make the logo bigger" always won, and it floored every result at the baseline, so the number could only go up. The version here fixes both:
diff --git a/backend/agents.py b/backend/agents.py
index c3b04cd..3cab981 100644
--- a/backend/agents.py
+++ b/backend/agents.py
@@ -16,6 +16,8 @@
import branch
import competitive
+import compose
+from branch_pool import branches
import deepgaze_runner as dg
import eval_guard
import gemini
@@ -44,42 +46,17 @@ def scout(brand: str, brief: dict) -> dict:
return _SCOUT_CACHE[key]
+def edit_branch(image: Image.Image, branch_: dict) -> tuple[Image.Image, str]:
+ """Run one branch edit and keep only its real changes near the branch's region."""
+ variant, desc = gemini.edit_image(image, branch_["directive"])
+ if str(desc).startswith("["):
+ return variant, desc
+ return compose.keep_changes(image, variant, branch_["region"], branch_.get("protect")), desc
+
+
def _directive_pool(before: dict, insights: dict, brand: str) -> list[str]:
- """Diverse edit hypotheses, spanning safe→moderately strong. ENHANCEMENT-first so the
- winner looks like a *better ad*, not a scorched background with a floating logo; the
- Judge-gated fitness (_make_scorer) penalizes any variant that crosses into garish, so
- the search can push moderately without an ugly edit winning on raw attention."""
- target = f"the {brand} logo and product"
- # Content-level levers that actually move DeepGaze (gentle re-lighting barely does). The first
- # three run on the live (depth=1) path, so they're the highest-value, most distinct edits:
- # de-clutter, reframe head-on, add brand text. Judge-gated fitness still vetoes un-ad-like edits.
- pool = [
- f"make SEVERAL coordinated changes at once to turn this into a polished campaign: remove the "
- f"clutter and any objects, hands, props or stray text crowding or blocking {target}; reframe "
- f"it head-on and enlarge it as the clear hero; clean and simplify the background; and add a "
- f"bold, legible on-brand headline, call-to-action and wordmark on {target}",
- f"remove or clean away any objects, hands, props or background obstructions that crowd or "
- f"block {target} so the product is fully visible, unobstructed, and the clear hero of the shot",
- f"reframe {target} to a clean, head-on, front-facing hero angle — square it to the camera so "
- f"it faces the viewer directly and reads instantly — keep it in roughly the same spot",
- f"add a bold, legible, on-brand headline and call-to-action and strengthen the logo/wordmark "
- f"right at {target} as a strong, high-contrast focal point",
- f"clearly enlarge, brighten and sharpen {target} so it becomes the single biggest, boldest "
- f"focal element while gently dimming and de-cluttering the surroundings — a polished, real ad",
- f"recolor the background to a clean, on-brand solid or subtle gradient so {target} stands out "
- f"as the hero, keeping the shot photographic and realistic",
- ]
- # Targeted suppression of each named competing element (clearly tone down, not blackout).
- pool += [f"clearly tone down or remove the {d['desc']} — it is stealing attention from "
- f"{target}; reduce its brightness, color and prominence so the brand wins the eye"
- for d in before["distractors"]]
- pool += [t["apply"] for t in insights.get("tactics", [])]
- seen, uniq = set(), []
- for d in pool:
- if d and d not in seen:
- seen.add(d)
- uniq.append(d)
- return uniq
+ """Directive strings for the branching search (see `branches`)."""
+ return [b["directive"] for b in branches(before, brand)]
def _serialize_tree(tree: list[dict]) -> list[dict]:
@@ -131,7 +108,8 @@ def _step_prep(state: dict) -> dict:
def _step_context(state: dict) -> dict:
"""Distractor naming + competitor RAG (Scout) — independent, run concurrently."""
with cf.ThreadPoolExecutor(max_workers=2) as ex:
- f_names = ex.submit(gemini.label_distractors, state["image"], state["before"]["distractors"])
+ f_names = ex.submit(gemini.label_distractors, state["image"], state["before"]["distractors"],
+ state["brand"])
f_insights = ex.submit(scout, state["brand"], state["brief"])
f_names.result()
state["insights"] = f_insights.result()
@@ -140,7 +118,9 @@ def _step_context(state: dict) -> dict:
def _step_optimize(state: dict) -> dict:
"""Retoucher: branching tree-search over edits, scored by the Judge-gated fitness."""
- pool = _directive_pool(state["before"], state["insights"], state["brand"])
+ pool_ = branches(state["before"], state["brand"])
+ pool = [b["directive"] for b in pool_]
+ by_directive = {b["directive"]: b for b in pool_}
def propose(_img, node, n):
start = (node["depth"] * n) % len(pool) # rotate so each round tries fresh avenues
@@ -149,7 +129,7 @@ def propose(_img, node, n):
judged: dict[int, dict] = {}
state["result"] = branch.search(
state["image"], state["baseline"], propose,
- edit=lambda img, directive: gemini.edit_image(img, directive),
+ edit=lambda img, directive: edit_branch(img, by_directive[directive]),
score=_make_scorer(state["target"], state["brand"], judged),
breadth=settings.breadth, max_depth=state["depth"], beam_width=1,
target_score=settings.target_score, epsilon=settings.epsilon,
diff --git a/backend/branch_pool.py b/backend/branch_pool.py
new file mode 100644
index 0000000..ec45ee7
--- /dev/null
+++ b/backend/branch_pool.py
@@ -0,0 +1,38 @@
+"""The branch edits Pixel tries on an ad. Plain data, no model calls, so it is easy to test."""
+from __future__ import annotations
+
+
+def branches(before: dict, brand: str) -> list[dict]:
+ """Branch edits for one image, strongest first. Each is {"directive", "region",
+ "protect"}: one concrete change, the normalized boxes the change must stay near, and
+ the boxes it must never touch (see compose.keep_changes).
+
+ Every branch removes one attention thief, the strongest first, because that is the
+ edit the measurements back: on five ads, removing the named thief beat the original
+ in 9 of 10 tries (mean +2.1 points), while the old presets (headline, CTA, several
+ changes at once, dimming the surroundings) won 2 of 20 (mean -9.6). See
+ docs/PIXEL_FINDINGS.md. Last comes a small polish of the product itself, which rarely
+ wins but is the only honest move left on an ad with no thieves. No branch adds text
+ or dims the scene."""
+ out = []
+ for d in before.get("distractors", []):
+ if d.get("own"): # the brand's own slogan or logo pulling the eye is not a thief
+ continue
+ what = d["desc"]
+ if what.endswith(" region"): # unnamed: Gemini couldn't label it
+ what = f"distracting object in the {what} of the photo"
+ out.append({
+ "directive": f"remove the {what} completely and fill its area with what would "
+ f"naturally be behind it. Keep the {brand} ad, product, logo and slogan "
+ f"exactly as they are",
+ "region": [d["region"]],
+ "protect": [before["target_box"]],
+ })
+ # Asked for a crisper "label", the model wrote a new one, so this names no text at all.
+ out.append({
+ "directive": f"make the surface of the {brand} product a little crisper and cleaner with "
+ f"clean highlights, without adding, changing or removing any text or logo",
+ "region": [before["target_box"]],
+ "protect": [],
+ })
+ return out
diff --git a/backend/compose.py b/backend/compose.py
new file mode 100644
index 0000000..65670d0
--- /dev/null
+++ b/backend/compose.py
@@ -0,0 +1,106 @@
+"""Keep a generated edit only where it was meant to happen.
+
+Nano Banana returns a whole new frame even for a one-object edit: it shifts the picture
+by a few pixels, re-renders textures and nudges colors everywhere. On the recorded runs
+that drift alone moved the score by a few points either way. So each branch edit is
+registered back onto the original (translation only), and only the pixels that really
+changed near the branch's target are pasted in. A fixed box around the thief was not
+enough: it cut objects in half and left seams, so the mask follows the actual change.
+Every other pixel stays identical to the original.
+"""
+from __future__ import annotations
+
+import numpy as np
+from PIL import Image, ImageDraw, ImageFilter
+from scipy.ndimage import binary_opening, find_objects, gaussian_filter, label
+
+REG_SIDE = 512 # register at this resolution
+MAX_SHIFT = 0.05 # ignore a "registration" bigger than this fraction of the frame
+GROW = 0.04 # grow the branch region by this fraction of the frame
+NEAR = 0.08 # a changed pixel counts only within this distance of the branch region
+DIFF_SIDE = 256 # resolution of the change map
+DIFF_THRESH = 24 # mean RGB difference (0..255) that counts as a real change
+BLOB_PAD = 0.015 # grow each changed blob's box by this fraction of the frame
+FEATHER = 0.012 # feather radius, fraction of the long side
+
+
+def _gray(img: Image.Image, size) -> np.ndarray:
+ return np.asarray(img.convert("L").resize(size, Image.BILINEAR), dtype=np.float64)
+
+
+def estimate_shift(orig: Image.Image, edited: Image.Image) -> tuple[float, float]:
+ """(dx, dy) in original pixels such that edited(x + dx, y + dy) ~ orig(x, y),
+ by phase correlation. Returns (0, 0) when the peak is implausibly far."""
+ w, h = orig.size
+ s = REG_SIDE / max(w, h)
+ size = (max(16, int(w * s)), max(16, int(h * s)))
+ a, b = _gray(orig, size), _gray(edited, size)
+ win = np.outer(np.hanning(size[1]), np.hanning(size[0]))
+ fa, fb = np.fft.fft2((a - a.mean()) * win), np.fft.fft2((b - b.mean()) * win)
+ r = fb * np.conj(fa)
+ corr = np.fft.ifft2(r / (np.abs(r) + 1e-9)).real
+ dy, dx = np.unravel_index(int(corr.argmax()), corr.shape)
+ if dy > size[1] // 2:
+ dy -= size[1]
+ if dx > size[0] // 2:
+ dx -= size[0]
+ if abs(dx) > MAX_SHIFT * size[0] or abs(dy) > MAX_SHIFT * size[1]:
+ return 0.0, 0.0
+ return dx / s, dy / s
+
+
+def register(orig: Image.Image, edited: Image.Image) -> Image.Image:
+ """Shift `edited` so it lines up with `orig`."""
+ edited = edited.convert("RGB").resize(orig.size, Image.LANCZOS)
+ dx, dy = estimate_shift(orig, edited)
+ if dx == 0 and dy == 0:
+ return edited
+ return edited.transform(orig.size, Image.AFFINE, (1, 0, dx, 0, 1, dy), resample=Image.BILINEAR)
+
+
+def region_mask(size, regions, grow: float = GROW, feather: float = FEATHER) -> Image.Image:
+ """Feathered mask covering the normalized [x, y, w, h] regions, each grown by `grow`."""
+ w, h = size
+ m = Image.new("L", size, 0)
+ d = ImageDraw.Draw(m)
+ for x, y, bw, bh in regions:
+ d.rectangle([(x - grow) * w, (y - grow) * h, (x + bw + grow) * w, (y + bh + grow) * h], fill=255)
+ return m.filter(ImageFilter.GaussianBlur(feather * max(w, h)))
+
+
+def change_mask(orig: Image.Image, edited: Image.Image, near, protect=None) -> Image.Image:
+ """Feathered mask of where `edited` really differs from `orig`: blurred at low
+ resolution so jitter drops out, limited to within NEAR of the `near` regions, and
+ never inside the `protect` regions (the brand's own box)."""
+ w, h = orig.size
+ s = DIFF_SIDE / max(w, h)
+ size = (max(16, int(w * s)), max(16, int(h * s)))
+ a = np.asarray(orig.convert("RGB").resize(size, Image.BILINEAR), np.float64)
+ b = np.asarray(edited.convert("RGB").resize(size, Image.BILINEAR), np.float64)
+ m = gaussian_filter(np.abs(a - b).mean(2), 1.5) > DIFF_THRESH
+ m = binary_opening(m, iterations=1)
+ allow = np.asarray(region_mask(size, near, grow=NEAR, feather=0)) > 0
+ if protect:
+ allow &= ~(np.asarray(region_mask(size, protect, grow=0.0, feather=0)) > 0)
+ m &= allow
+ # A removed object's faint parts (a phone's edge over a keyboard, the thin strokes of a
+ # sign) fall under the threshold and came back as ghosts. So every changed blob is
+ # replaced by its whole bounding box, grown a little, before feathering.
+ labels, n = label(m)
+ full = np.zeros_like(m)
+ pad = max(2, int(BLOB_PAD * max(size)))
+ for sl in find_objects(labels):
+ if sl is None:
+ continue
+ ys, xs = sl
+ full[max(0, ys.start - pad):ys.stop + pad, max(0, xs.start - pad):xs.stop + pad] = True
+ full &= allow
+ mi = Image.fromarray((full * 255).astype(np.uint8)).resize((w, h), Image.BILINEAR)
+ return mi.filter(ImageFilter.GaussianBlur(FEATHER * 0.5 * max(w, h)))
+
+
+def keep_changes(orig: Image.Image, edited: Image.Image, near, protect=None) -> Image.Image:
+ """The original, with only the real changes of the registered edit near `near` pasted in."""
+ orig = orig.convert("RGB")
+ reg = register(orig, edited)
+ return Image.composite(reg, orig, change_mask(orig, reg, near, protect))
diff --git a/backend/deepgaze_runner.py b/backend/deepgaze_runner.py
index b575f8a..5cd12c5 100644
--- a/backend/deepgaze_runner.py
+++ b/backend/deepgaze_runner.py
@@ -252,7 +252,7 @@ def _scanpath(density: np.ndarray, n: int = 5, sigma_frac: float = 0.08) -> list
return out
-def predict(image: Image.Image, target_box: Box | None = None) -> dict:
+def predict(image: Image.Image, target_box: Box | None = None, n_thieves: int = 2) -> dict:
"""Full /predict payload for one image. `attention_score` is the size-invariant
prominence; `target_salience` is the absolute attention mass on target (used by the
reward-hack guard to tell a real improvement from 'suppress everything else')."""
@@ -268,7 +268,7 @@ def predict(image: Image.Image, target_box: Box | None = None) -> dict:
"heatmap_png": _heatmap_data_url(density),
"target_box": [round(v, 4) for v in target],
"scanpath": _scanpath(density),
- "distractors": _top_distractors(density, target),
+ "distractors": _top_distractors(density, target, k=n_thieves),
"engine": _engine,
}
diff --git a/backend/eval_guard.py b/backend/eval_guard.py
index 36eea1a..330d000 100644
--- a/backend/eval_guard.py
+++ b/backend/eval_guard.py
@@ -47,6 +47,9 @@
GLOBAL_COVERAGE = 0.55 # fraction of pixels changed above which an edit looks "global"
GLOBAL_BBOX = 0.60 # change bounding-box covering more of the frame than this = "global"
EPS = 1e-4
+# Smallest score rise that counts as a win. Regenerating a region with no visible change
+# moved the score by up to about half a point on the recorded edits, so smaller rises are noise.
+MIN_GAIN = 0.005
GRID = 8 # cells per side for the rest-of-frame comparison
OUTSIDE_MARGIN = 0.04 # grow the target box by this before measuring "the rest of the frame"
# Floors for the rest of the frame, as after/before ratios. Below any of these, the edit
@@ -219,7 +222,7 @@ def verdict(
"""
pc = perceptual_change(before, after)
reasons: list[str] = []
- ratio_gain = (ratio_after - ratio_before) > EPS
+ ratio_gain = (ratio_after - ratio_before) > MIN_GAIN
have_abs = target_sal_before is not None and target_sal_after is not None
real_gain = have_abs and (target_sal_after - target_sal_before) > EPS
suppression = have_abs and ratio_gain and not real_gain
diff --git a/backend/gemini.py b/backend/gemini.py
index 00a398f..8ce219f 100644
--- a/backend/gemini.py
+++ b/backend/gemini.py
@@ -31,18 +31,16 @@ def _genai():
return _client
-# Focus-oriented edit template. We optimize attention onto the brand, so the editor MAY make
-# real content changes — enlarge/brighten the logo, add a bold CTA, remove clutter — not just
-# dim the background. Only constraint: keep the brand element roughly where it is and keep the
-# overall aspect ratio (so the before/after attention score stays comparable).
+# Edit template. Measured on the recorded runs (docs/PIXEL_FINDINGS.md): text is the
+# strongest attractor DeepGaze knows, so a new headline or button outside the brand box
+# cost 5 to 22 points, and "make it a polished ad" made the model redraw the whole frame.
+# So the editor gets one concrete, local change and is told not to add text or touch
+# anything else. Dimming the scene is not offered either; the guard rejects it anyway.
_EDIT_TMPL = (
- "You are a senior art director optimizing this ad so the brand's logo / product / "
- "call-to-action is the first thing the eye lands on. Apply this change: {directive}. "
- "You MAY enlarge, brighten and sharpen the brand logo and product, add a clear bold "
- "on-brand call-to-action or wordmark, and remove or simplify clutter and competing "
- "elements. Keep the brand element roughly centered where it already sits (do not move it "
- "to a different part of the frame) and do NOT crop or change the overall aspect ratio. "
- "Return one polished, realistic, on-brand ad."
+ "Edit this ad photo. Apply exactly this one change: {directive}. "
+ "Do not add any text, letters, words, logos, slogans, buttons, badges or graphics. "
+ "Do not change the lighting, colors, camera angle, framing or crop, and do not move "
+ "or resize the product. Leave every other part of the photo exactly as it is."
)
@@ -135,10 +133,12 @@ def brand_brief(brand: str) -> dict:
return dict(_FALLBACK_BRIEF)
-def name_regions(image: Image.Image, regions: list[list[float]]) -> list[str]:
+def name_regions(image: Image.Image, regions: list[list[float]], brand: str = "") -> list[dict]:
"""Label what's actually inside each normalized [x,y,w,h] box (e.g. 'woman's face',
'Coca-Cola can') so distractor callouts read like a human, not 'upper-left region'.
- Returns [] on no key/error — callers keep their own position labels."""
+ With a brand, also say whether each crop is part of that brand's own ad (its product,
+ logo, slogan or tagline), which a removal branch must never touch.
+ Returns [{"name", "own"}] per region, or [] on no key/error."""
client = _genai()
if client is None or not regions:
return []
@@ -147,25 +147,30 @@ def name_regions(image: Image.Image, regions: list[list[float]]) -> list[str]:
W, H = image.size
crops = [image.crop((int(x * W), int(y * H), int((x + w) * W), int((y + h) * H)))
for x, y, w, h in regions]
+ own = (f' and "own":[true/false,...], true when the crop shows part of the {brand} ad '
+ f"itself (its product, logo, slogan or tagline)") if brand else ""
prompt = (
f"Here are {len(crops)} crops from one ad, in order. Name the main object in each "
- 'crop in 2-4 words. Respond ONLY JSON {"labels":["..","..."]} in the same order.'
+ f'crop in 2-4 words. Respond ONLY JSON {{"labels":["..",".."]{own}}} in the same order.'
)
try:
resp = client.models.generate_content(model=settings.gemini_text_model, contents=[prompt, *crops])
- labels = json.loads(_first_json(resp.text)).get("labels", [])
- return [str(x) for x in labels][: len(regions)]
+ j = json.loads(_first_json(resp.text))
+ labels, owns = j.get("labels", []), j.get("own", [])
+ return [{"name": str(n), "own": bool(owns[i]) if i < len(owns) else False}
+ for i, n in enumerate(labels[: len(regions)])]
except Exception:
return []
-def label_distractors(image: Image.Image, distractors: list[dict]) -> None:
+def label_distractors(image: Image.Image, distractors: list[dict], brand: str = "") -> None:
"""Replace each distractor's position label ('upper-left region') with the named
- object Gemini sees there ('woman's face'). Mutates in place; no-op without a key."""
- names = name_regions(image, [d["region"] for d in distractors])
- for d, nm in zip(distractors, names):
- if nm:
- d["desc"] = nm
+ object Gemini sees there ('woman's face'), and mark the ones that belong to the
+ brand's own ad. Mutates in place; no-op without a key."""
+ for d, nm in zip(distractors, name_regions(image, [d["region"] for d in distractors], brand)):
+ if nm["name"]:
+ d["desc"] = nm["name"]
+ d["own"] = nm["own"]
def _first_json(text: str) -> str:
diff --git a/backend/main.py b/backend/main.py
index ff065f7..bc15ad2 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -5,6 +5,7 @@
from __future__ import annotations
import base64
+import hashlib
import io
import json
from contextlib import asynccontextmanager
@@ -92,43 +93,47 @@ async def run_agents(image: UploadFile = File(...), brand: str = Form("the brand
return agents.run(await _image(image), brand, box)
-def _stepwise_order(pool: list[str]) -> list[str]:
- """Rank directives so the first branches are the most reliable attention-raisers.
- 0 = amplify the brand (enlarge/brighten/add CTA), 1 = neutral, 2 = risky/structural."""
- def rank(d: str) -> int:
- head = d.lower().lstrip() # rank by the leading verb, not mid-sentence words
- if head.startswith(("add", "enlarge", "clearly enlarge", "boost", "make", "brighten")):
- return 0 # amplify the brand — most reliable lift
- if head.startswith(("remove", "reframe", "recolor", "clearly tone", "tone")):
- return 2 # structural / risky — can regress, try later
- return 1
- return sorted(pool, key=rank)
+# Gemini names the same thief differently on each call ("white phone back", "back of
+# phone"), so an unchanged image kept retrying the same removal under a new name. Names
+# are cached per image and region, which keeps the "tried" list meaningful.
+_LABELS: dict[tuple, list[tuple[str, bool]]] = {}
+
+
+def _label_once(img: Image.Image, distractors: list[dict], brand: str) -> None:
+ key = (hashlib.md5(img.tobytes()).hexdigest(), brand, tuple(tuple(d["region"]) for d in distractors))
+ if key not in _LABELS:
+ gemini.label_distractors(img, distractors, brand)
+ _LABELS[key] = [(d["desc"], d.get("own", False)) for d in distractors]
+ for d, (name, own) in zip(distractors, _LABELS[key]):
+ d["desc"], d["own"] = name, own
@app.post("/optimize/step")
async def optimize_step(image: UploadFile = File(...), brand: str = Form("the brand"),
target: str | None = Form(None), step: int = Form(0),
- hint: str = Form("")) -> dict:
+ hint: str = Form(""), tried: str = Form("[]")) -> dict:
"""One branch at a time. `image` is the CURRENT best creative (the original on step 0);
the frontend re-sends the adopted winner each step. We run a single Nano Banana edit
(next directive in the pool), re-score with DeepGaze, and Judge it. The frontend shows
- this one branch and asks the user whether to spawn another."""
+ this one branch and asks the user whether to spawn another. `tried` is a JSON list of
+ the directives already run on this ad, so each step takes the next untried branch."""
img = await _image(image)
box = json.loads(target) if target else None
- before = dg.predict(img, box)
+ before = dg.predict(img, box, n_thieves=3)
+ _label_once(img, before["distractors"], brand) # branches name what to remove
tbox = box or before["target_box"]
current = before["attention_score"]
abs_before = before["target_salience"]
- # One branch at a time, so ORDER matters. Lead with stronger edits before the riskier
- # structural ones (remove/reframe/recolor) that can regress. The score is size-invariant
- # prominence, so a strong edit no longer trivially wins — the real delta can go negative
- # and is reported as such.
- pool = _stepwise_order(agents._directive_pool(before, {}, brand))
- directive = pool[step % len(pool)]
+ # One branch at a time: the strongest attention thief on the CURRENT image that hasn't
+ # been tried yet. After a kept removal the next thief surfaces on its own.
+ done = " ".join(json.loads(tried or "[]"))
+ pool = agents.branches(before, brand)
+ fresh = [b for b in pool if b["directive"] not in done] or [pool[step % len(pool)]]
+ pick = dict(fresh[0])
if hint.strip(): # the user's own suggestion, applied on top of the auto edit
- directive = f"{directive}. Also apply the user's request: {hint.strip()}"
- variant, desc = gemini.edit_image(img, directive)
+ pick["directive"] = f"{pick['directive']}. Also apply the user's request: {hint.strip()}"
+ variant, desc = agents.edit_branch(img, pick)
really_edited = not str(desc).startswith("[")
# Both axes in one pass: size-invariant prominence + absolute on-target salience.
new_score, abs_after = dg.score_components(variant, tbox)
@@ -162,7 +167,7 @@ async def optimize_step(image: UploadFile = File(...), brand: str = Form("the br
# Honest: improved only when the real score rose, the Judge didn't veto it, AND the
# guard confirms a real perceptible on-target gain (not suppression / invisible edit).
"improved": bool(new_score > current and not vetoed and accepted),
- "n_directives": len(pool),
+ "n_directives": step + len(fresh), # this branch plus the untried ones left
}
diff --git a/backend/test_branches.py b/backend/test_branches.py
new file mode 100644
index 0000000..7b36764
--- /dev/null
+++ b/backend/test_branches.py
@@ -0,0 +1,79 @@
+"""Tests for the branch edits and for keeping each edit inside its region.
+
+Run: python -m pytest test_branches.py (or) python test_branches.py
+"""
+from __future__ import annotations
+
+import re
+
+import numpy as np
+from PIL import Image, ImageFilter
+
+import compose
+from branch_pool import branches
+
+_RNG = np.random.default_rng(2)
+_ORIG = Image.fromarray(_RNG.integers(0, 255, (300, 200, 3)).astype("uint8")).filter(ImageFilter.GaussianBlur(2))
+_BEFORE = {
+ "target_box": [0.35, 0.3, 0.3, 0.3],
+ "distractors": [
+ {"desc": "Subway sign", "share": 0.36, "region": [0.4, 0.75, 0.2, 0.2]},
+ {"desc": "lower-left region", "share": 0.07, "region": [0.0, 0.8, 0.2, 0.2]},
+ ],
+}
+
+
+def test_one_removal_branch_per_thief_then_a_polish():
+ pool = branches(_BEFORE, "Nike")
+ assert len(pool) == 3
+ assert "Subway sign" in pool[0]["directive"] and pool[0]["region"] == [_BEFORE["distractors"][0]["region"]]
+ assert "lower-left region of the photo" in pool[1]["directive"] # unnamed thief still targeted
+ assert pool[2]["region"] == [_BEFORE["target_box"]]
+ assert pool[0]["protect"] == [_BEFORE["target_box"]] # a removal never touches the brand
+
+
+def test_no_branch_adds_text_or_dims_the_scene():
+ banned = ("headline", "call-to-action", "cta", "wordmark", "tagline", "dim", "darken",
+ "tone down", "desaturate", "blur")
+ for b in branches(_BEFORE, "Nike"):
+ d = b["directive"].lower()
+ assert not any(w in d for w in banned), d
+ assert not re.search(r"add", d), d # never asks to add anything
+
+
+def test_brand_own_elements_are_never_removed():
+ before = dict(_BEFORE, distractors=[{"desc": "Just do it slogan", "share": 0.1, "own": True,
+ "region": [0.3, 0.7, 0.2, 0.1]}])
+ assert len(branches(before, "Nike")) == 1 # only the product polish is left
+
+
+def test_ad_with_no_thieves_still_gets_a_branch():
+ assert len(branches({"target_box": [0.3, 0.3, 0.3, 0.3], "distractors": []}, "Pepsi")) == 1
+
+
+def test_keep_changes_takes_only_real_changes_near_the_region_and_off_the_brand():
+ a = np.asarray(_ORIG).copy()
+ edited = a.copy()
+ edited[200:260, 20:80] = 0 # the removal, inside the branch region
+ edited[20:60, 140:190] = 255 # drift far from the region
+ edited[100:140, 80:110] = 255 # a change on the brand box
+ out = np.asarray(compose.keep_changes(_ORIG, Image.fromarray(edited),
+ near=[[0.1, 0.65, 0.3, 0.2]], protect=[[0.35, 0.3, 0.3, 0.3]]))
+ assert out[230, 50].max() < 40 # removal kept
+ assert (out[40, 165] == a[40, 165]).all() # far drift dropped
+ assert (out[120, 95] == a[120, 95]).all() # brand untouched
+
+
+def test_register_undoes_a_small_shift():
+ a = np.asarray(_ORIG)
+ shifted = Image.fromarray(np.roll(a, (6, 9), axis=(0, 1)))
+ back = np.asarray(compose.register(_ORIG, shifted)).astype(int)
+ assert np.abs(back[20:-20, 20:-20] - a[20:-20, 20:-20].astype(int)).mean() < 4
+
+
+if __name__ == "__main__":
+ fns = [v for k, v in sorted(globals().items()) if k.startswith("test_") and callable(v)]
+ for fn in fns:
+ fn()
+ print(f"ok {fn.__name__}")
+ print(f"\n{len(fns)} branch tests passed.")
diff --git a/docs/PIXEL_FINDINGS.md b/docs/PIXEL_FINDINGS.md
new file mode 100644
index 0000000..030f912
--- /dev/null
+++ b/docs/PIXEL_FINDINGS.md
@@ -0,0 +1,81 @@
+# Why most edits lost, and what wins now
+
+October 2026. Every number here is measured with DeepGaze IIE on the local GPU, on the seven sample ads, unless it says otherwise.
+
+## The bottleneck
+
+There were three problems. Each one alone was enough to sink most edits.
+
+**1. The edits added text, and text is the strongest thing DeepGaze looks at.** The old branch presets were "make several changes at once", "add a headline and call-to-action", "enlarge and brighten while gently dimming the surroundings", "remove clutter", and on top of every one the edit template said "you MAY add a bold call-to-action or wordmark" and "return a polished ad". So nearly every edit came back with a slogan or a button. I pasted one plain headline onto each original, outside the brand box. It cost 5 to 22 points on six of seven ads (Spotify -21.7, Pepsi -17.5, Coca-Cola -13.4). Put on the product itself, it still cost 4 to 6 points.
+
+**2. Nano Banana redraws the whole frame.** Even for a one-object edit it shifts the picture by a few pixels and re-renders textures and colors everywhere. On the recorded preset edits, up to 94% of the frame outside the brand box changed structure. When I pasted back only the brand-box part of each recorded edit, the average change went from -11.8 points to -2.4, and 7 of 26 edits turned positive. Most of the damage happened outside the box.
+
+**3. Several ads have almost no headroom.** The score is `r / (r + 1)`, where `r` is the attention inside the box divided by the box's share of the frame. Even if 100% of the attention lands in the box, the score can't go above `1 / (1 + box area)`.
+
+| Ad | Score now | Best possible | Headroom |
+|---|---|---|---|
+| Nike | 59.2 | 88.1 | +28.9 |
+| Red Bull | 79.0 | 88.5 | +9.5 |
+| Spotify | 76.0 | 83.3 | +7.4 |
+| Apple | 75.8 | 81.8 | +5.9 |
+| Coca-Cola | 80.3 | 82.6 | +2.3 |
+| McDonald's | 83.8 | 85.4 | +1.5 |
+| Pepsi | 85.5 | 86.7 | +1.2 |
+
+On Coca-Cola, McDonald's and Pepsi the brand already holds 86 to 90% of the attention, so the most any edit can gain is 1 to 2 points. Losing there is the honest answer, not a failure of the agents.
+
+Smaller findings:
+- **Making the product bigger doesn't help.** I scaled it 1.25x in place. With the box fixed, 5 of 7 ads lost (the product spills out of the box). With the box grown to match, all 7 lost 4 to 7 points. The size-invariant score is doing its job.
+- **Moving the product toward the first fixation doesn't apply.** The first predicted fixation already lands in the brand box on 6 of 7 ads. Nike is the exception, and a billboard can't be moved.
+- **A contrast and color pop on the product alone** gave +0.7 to +1.0 on four ads and nothing on the saturated ones. It's real, but too small to matter.
+- **Cropping** helped only Nike (+17.3, by cutting the store signs out of the frame). It lost or did nothing on the other six, and it changes the brand box, so I left it out.
+- **The Judge vetoes very little.** It gave the unedited originals 0.9 to 1.0, except Pepsi at 0.25, so it's noisy. It vetoed 2 of the 26 recorded preset edits I analyzed. It isn't the bottleneck.
+- **The guard** rejected the old wins that dimmed the scene (see `backend/eval_guard.py`). That's correct, and it isn't why honest edits lost.
+
+## What I tried
+
+Four strategies on five ads (Nike, Red Bull, Spotify, Apple, Coca-Cola). Every edit used a template that forbids adding text and forbids touching anything else. They're scored against the old presets on the same five ads.
+
+| Strategy | Beat the original | Mean change |
+|---|---|---|
+| Old presets (recorded runs, current guard) | 2 of 20 | -9.6 |
+| Remove the named attention thief | 9 of 10 | +2.1 |
+| Remove every prop in the scene | 1 of 5 | -0.2 |
+| Group the props together, away from the product | 0 of 5 | -4.5 |
+| Polish the product itself | 1 of 5 | -0.3 |
+
+- **Grouping** was Enes's idea, and it failed for a mechanical reason: Nano Banana can't move things. Asked to gather the props in one corner, it pasted a shrunken copy of the hand and phone at the top of the frame, or added a "Modell's" sign.
+- **Removing every prop** at once got the list from a Gemini inventory. That list included "water", "trees", "pants" and "background", and the model either did nothing or redrew the scene.
+- **Best-of-N** (two candidates per branch, keep the higher score) looked attractive, but on Nike the higher-scoring candidate had replaced the bottom half of the billboard with a building. Picking by score alone rewards exactly that kind of damage, so I left it out.
+
+## What ships
+
+Branches remove the attention thieves one at a time, strongest first (`backend/branch_pool.py`):
+
+1. DeepGaze finds the strongest attention peaks outside the brand box.
+2. Gemini names each one from a crop, and says whether it belongs to the brand's own ad. The brand's own slogan or logo is never removed; an early run erased "Just do it." and called it a win.
+3. Nano Banana removes that one thing, with no text and no other changes.
+4. The edit is registered back onto the original. Only the pixels that really changed near the thief are pasted in (`backend/compose.py`), never the brand box. Everything else stays identical. A plain box around the thief cut objects in half, and a raw change mask left ghosts of removed objects, so each changed blob is pasted as a whole.
+5. DeepGaze re-scores the result. The guard and Judge decide as before. A rise under half a point counts as noise, because re-rendering a region with no visible change moved the score that much.
+
+If a branch wins, the next step works on the new image, and the next thief surfaces on its own.
+
+Re-recorded demo runs (25 branches, 9 kept):
+
+| Ad | Kept | Score |
+|---|---|---|
+| Nike | 4 of 5 | 59.2 to 77.0 |
+| Red Bull | 2 of 5 | 79.0 to 81.0 |
+| Spotify | 1 of 5 | 76.0 to 78.1 |
+| Apple | 1 of 5 | 75.8 to 77.3 |
+| Coca-Cola | 1 of 3 | 80.3 to 80.8 |
+| McDonald's | 0 of 1 | no thieves, at its ceiling |
+| Pepsi | 0 of 1 | no thieves, at its ceiling |
+
+On Nike, the kept branches remove the Modell's/Subway storefront sign, a No Turns sign, the Swarovski logo and an A$AP Ferg poster, while the billboard and "Just do it." stay untouched. I checked every kept branch by eye.
+
+## Limits
+
+- Gemini's thief names are noisy ("white phone back" for a mouse, "blurred background" for an empty patch). A badly named thief usually produces no change, and the guard rejects it as imperceptible.
+- Removing things helps most when there is clutter. A clean product shot is already near its ceiling, and no honest edit moves it much.
+- Phase 2 used 80 image edits in all: 25 for the strategy experiments and 55 for building and re-recording the demo.
diff --git a/frontend/public/replay/apple/predict.json b/frontend/public/replay/apple/predict.json
index 42e7a5a..35834dc 100644
--- a/frontend/public/replay/apple/predict.json
+++ b/frontend/public/replay/apple/predict.json
@@ -45,7 +45,7 @@
0.2
],
"share": 0.09,
- "desc": "Smartwatch band"
+ "desc": "perforated watch band"
},
{
"region": [
@@ -55,7 +55,7 @@
0.2
],
"share": 0.027,
- "desc": "Abstract blurred image"
+ "desc": "blurred reflection"
}
]
}
\ No newline at end of file
diff --git a/frontend/public/replay/apple/step0-heat.png b/frontend/public/replay/apple/step0-heat.png
index d37f6bd..72ad1dd 100644
Binary files a/frontend/public/replay/apple/step0-heat.png and b/frontend/public/replay/apple/step0-heat.png differ
diff --git a/frontend/public/replay/apple/step0.jpg b/frontend/public/replay/apple/step0.jpg
index e33977e..2f551ce 100644
Binary files a/frontend/public/replay/apple/step0.jpg and b/frontend/public/replay/apple/step0.jpg differ
diff --git a/frontend/public/replay/apple/step1-heat.png b/frontend/public/replay/apple/step1-heat.png
index 1982d93..da1549a 100644
Binary files a/frontend/public/replay/apple/step1-heat.png and b/frontend/public/replay/apple/step1-heat.png differ
diff --git a/frontend/public/replay/apple/step1.jpg b/frontend/public/replay/apple/step1.jpg
index 2362331..001e976 100644
Binary files a/frontend/public/replay/apple/step1.jpg and b/frontend/public/replay/apple/step1.jpg differ
diff --git a/frontend/public/replay/apple/step2-heat.png b/frontend/public/replay/apple/step2-heat.png
index 3ee5963..786d4cb 100644
Binary files a/frontend/public/replay/apple/step2-heat.png and b/frontend/public/replay/apple/step2-heat.png differ
diff --git a/frontend/public/replay/apple/step2.jpg b/frontend/public/replay/apple/step2.jpg
index 4509ccd..0d5f5f2 100644
Binary files a/frontend/public/replay/apple/step2.jpg and b/frontend/public/replay/apple/step2.jpg differ
diff --git a/frontend/public/replay/apple/step3-heat.png b/frontend/public/replay/apple/step3-heat.png
index 0e34d7d..c2c4c70 100644
Binary files a/frontend/public/replay/apple/step3-heat.png and b/frontend/public/replay/apple/step3-heat.png differ
diff --git a/frontend/public/replay/apple/step3.jpg b/frontend/public/replay/apple/step3.jpg
index e1f36aa..dc2018c 100644
Binary files a/frontend/public/replay/apple/step3.jpg and b/frontend/public/replay/apple/step3.jpg differ
diff --git a/frontend/public/replay/apple/step4-heat.png b/frontend/public/replay/apple/step4-heat.png
new file mode 100644
index 0000000..c2e2f33
Binary files /dev/null and b/frontend/public/replay/apple/step4-heat.png differ
diff --git a/frontend/public/replay/apple/step4.jpg b/frontend/public/replay/apple/step4.jpg
new file mode 100644
index 0000000..e658199
Binary files /dev/null and b/frontend/public/replay/apple/step4.jpg differ
diff --git a/frontend/public/replay/apple/steps.json b/frontend/public/replay/apple/steps.json
index 18bb9ee..8b5b54c 100644
--- a/frontend/public/replay/apple/steps.json
+++ b/frontend/public/replay/apple/steps.json
@@ -1,82 +1,102 @@
[
{
"step": 0,
- "directive": "make SEVERAL coordinated changes at once to turn this into a polished campaign: remove the clutter and any objects, hands, props or stray text crowding or blocking the Apple logo and product; reframe it head-on and enlarge it as the clear hero; clean and simplify the background; and add a bold, legible on-brand headline, call-to-action and wordmark on the Apple logo and product",
+ "directive": "remove the Sport watch band completely and fill its area with what would naturally be behind it. Keep the Apple ad, product, logo and slogan exactly as they are",
"variant_png": "replay/apple/step0.jpg",
"current_score": 0.7581,
- "new_score": 0.6945,
- "delta": -0.0636,
+ "new_score": 0.7732,
+ "delta": 0.0151,
"target_salience_before": 0.6983,
- "target_salience_after": 0.5065,
- "judge": 0.8,
- "judge_reason": "The creative exhibits strong brand-fit for Apple in terms of its aesthetic, messaging, and overall feel. The minimalist composition, aspirational slogan ('Experience More'), and clear call to action ('Learn More') are all characteristic of Apple's advertising. The imagery of the floating iPhone above a hand with an Apple Watch subtly highlights the ecosystem and a sense of effortless interaction or wonder. The photography quality and typography also align well with Apple's premium brand image. The main factor reducing a perfect score is the use of an iPhone X, which is an older model; current Apple ads almost exclusively feature their latest hardware. However, if this ad were created when the iPhone X was current, it would be an excellent example of an Apple campaign.",
+ "target_salience_after": 0.7594,
+ "judge": 0.7,
+ "judge_reason": "The creative concept of a floating iPhone is strong and visually engaging, aligning with Apple's aesthetic of wonder and seamless interaction. The composition effectively highlights the device. However, the specific iPhone model appears to be an older generation (likely X/XS era), and the screen content (apps, notifications, older app icons for some) and the slightly generic outdoor background lack the hyper-polished, meticulously controlled, and often minimalist presentation typical of Apple's flagship advertising campaigns. It has good potential but would likely require refinement in product generation, screen content, and overall production quality to meet Apple's stringent brand standards for a major ad.",
"vetoed": false,
- "guard": "reject",
+ "guard": "accept",
"guard_reasons": [
- "attention score did not improve"
+ "perceptible, localized/justified, and target salience improved"
],
- "improved": false,
- "n_directives": 8,
+ "improved": true,
+ "n_directives": 4,
"variant_heatmap": "replay/apple/step0-heat.png"
},
{
"step": 1,
- "directive": "add a bold, legible, on-brand headline and call-to-action and strengthen the logo/wordmark right at the Apple logo and product as a strong, high-contrast focal point",
+ "directive": "remove the Blurred building reflection completely and fill its area with what would naturally be behind it. Keep the Apple ad, product, logo and slogan exactly as they are",
"variant_png": "replay/apple/step1.jpg",
- "current_score": 0.7581,
- "new_score": 0.6853,
- "delta": -0.0728,
- "target_salience_before": 0.6983,
- "target_salience_after": 0.4851,
- "judge": 0.6,
- "judge_reason": "The ad features core Apple products (iPhone, Apple Watch, logo) and a relevant tagline 'CAPTURE YOUR WORLD' with a clear call to action, aligning with a key iPhone feature (camera). The composition is clean and the lighting is natural. However, it's unlikely to run as a real Apple ad due to several factors: the iPhone model appears to be an older generation (likely an iPhone X), whereas Apple typically features its latest models prominently in new campaigns. The concept of a levitating phone, while common in tech ads, lacks the unique, minimalist, and often more abstract or experiential creativity typically associated with official Apple campaigns. Furthermore, the overall production quality, while decent, doesn't quite reach the hyper-polished, studio-level photography and precise typography standards Apple is known for.",
- "vetoed": false,
+ "current_score": 0.7732,
+ "new_score": 0.7731,
+ "delta": -0.0001,
+ "target_salience_before": 0.7594,
+ "target_salience_after": 0.7591,
+ "judge": 0.3,
+ "judge_reason": "The creative has a visually interesting concept with the floating iPhone, suggesting advanced technology or a magical user experience, which aligns thematically with Apple's brand mystique. The product is clearly the focus, and the image quality (sharpness, depth of field) is good. However, it falls short of Apple's typical advertising standards for several reasons: the background is generic and lacks the highly curated, aspirational environments often seen in official Apple ads; the lighting, while soft, doesn't achieve the dramatic or iconic aesthetic Apple usually employs; and critically, it features an older iPhone model (likely iPhone X/XS era), which Apple would not use to advertise current products. It feels more like a high-quality stock photo or enthusiast content rather than an official, high-production-value Apple campaign.",
+ "vetoed": true,
"guard": "reject",
"guard_reasons": [
"attention score did not improve"
],
"improved": false,
- "n_directives": 8,
+ "n_directives": 5,
"variant_heatmap": "replay/apple/step1-heat.png"
},
{
"step": 2,
- "directive": "clearly enlarge, brighten and sharpen the Apple logo and product so it becomes the single biggest, boldest focal element while gently dimming and de-cluttering the surroundings \u2014 a polished, real ad",
+ "directive": "remove the Human hand palm completely and fill its area with what would naturally be behind it. Keep the Apple ad, product, logo and slogan exactly as they are",
"variant_png": "replay/apple/step2.jpg",
- "current_score": 0.7581,
- "new_score": 0.7858,
- "delta": 0.0277,
- "target_salience_before": 0.6983,
- "target_salience_after": 0.8174,
- "judge": 0.3,
- "judge_reason": "This creative attempts to capture Apple's focus on user experience, using a clean font and the iconic logo. However, several factors make it unlikely to run as a real Apple ad: \n1. **Outdated Product:** The ad features an iPhone X, which is an older model. Apple typically highlights its latest products in primary ad campaigns.\n2. **Quality & Polish:** The 'floating' effect and the glow around the phone appear somewhat amateurish and not seamlessly integrated. Apple ads are known for their extremely high production value, precision, and minimalist perfection, which this creative doesn't quite achieve.\n3. **Generic Message:** While 'EXPERIENCE' is a core tenet of Apple's brand, a single, broad word like this is often too generic for a standalone ad creative. Apple usually pairs its general ethos with specific features, innovations, or emotional benefits ('Privacy. That's iPhone,' 'Shot on iPhone,' 'The new way to interact with your phone').\n4. **Background:** The blurred background, while aiming for a lifestyle feel, lacks the distinctive or aspirational quality typical of Apple's curated environmental shots or their signature clean, often studio-like aesthetic.\n5. **Brand Fit (Aesthetics):** While the Apple logo and text font are on-brand, the overall execution lacks the sophisticated, premium feel Apple meticulously maintains in its advertising.",
- "vetoed": true,
+ "current_score": 0.7732,
+ "new_score": 0.7728,
+ "delta": -0.0004,
+ "target_salience_before": 0.7594,
+ "target_salience_after": 0.7575,
+ "judge": 0.85,
+ "judge_reason": "This creative demonstrates high quality with a sharp focus on the product, a clean aesthetic, and a compelling visual concept. The 'floating phone' effect, combined with the hand reaching for it, evokes a sense of wonder, effortlessness, or magic, which aligns well with Apple's aspirational and innovative brand identity. The blurred outdoor background provides a lifestyle context without distracting from the iPhone. Apple frequently uses striking, sometimes slightly surreal, imagery to highlight their products' design and user experience. While it's an older iPhone model, the creative direction is very much in line with how Apple presents its devices as elegant, advanced, and almost magical tools.",
+ "vetoed": false,
"guard": "reject",
"guard_reasons": [
- "raised the score by degrading the rest of the frame: darkened (0.68x), flattened (0.59x), desaturated (0.50x)"
+ "attention score did not improve"
],
"improved": false,
- "n_directives": 8,
+ "n_directives": 5,
"variant_heatmap": "replay/apple/step2-heat.png"
},
{
"step": 3,
- "directive": "remove or clean away any objects, hands, props or background obstructions that crowd or block the Apple logo and product so the product is fully visible, unobstructed, and the clear hero of the shot",
+ "directive": "remove the Blurred outdoor scene completely and fill its area with what would naturally be behind it. Keep the Apple ad, product, logo and slogan exactly as they are",
"variant_png": "replay/apple/step3.jpg",
- "current_score": 0.7581,
- "new_score": 0.7711,
- "delta": 0.013,
- "target_salience_before": 0.6983,
- "target_salience_after": 0.7506,
- "judge": 0.9,
- "judge_reason": "This creative demonstrates strong brand-fit for Apple. The minimalist aesthetic, featuring a clean product shot against a plain background, is a hallmark of Apple's advertising. The typography is simple and elegant, and the concise tagline 'CAPTURE THE MOMENT' directly highlights a key iPhone feature (the camera) and a core user benefit, aligning perfectly with Apple's benefit-driven messaging. While the phone is an older model (iPhone X/XS era), if evaluated within its historical context, the quality of the image and the clarity of the text are high. It's a straightforward, effective, and visually consistent ad that Apple would very likely run to promote the iPhone's camera capabilities.",
- "vetoed": false,
+ "current_score": 0.7732,
+ "new_score": 0.7713,
+ "delta": -0.0019,
+ "target_salience_before": 0.7594,
+ "target_salience_after": 0.7513,
+ "judge": 0.2,
+ "judge_reason": "The creative quality of the image itself is good, with a clear concept (levitating phone) and decent photography. However, it fails significantly on brand-fit for Apple as a primary ad creative today. The phone shown is an iPhone X, a model released in 2017. Apple's advertising almost exclusively features its latest flagship models or current generation products. Using a significantly outdated model for a hero creative would be highly unusual and off-brand for Apple, as it doesn't showcase their current innovation or product line. The 'magic' concept is fine, but the outdated device makes it unsuitable for real-world Apple advertising.",
+ "vetoed": true,
"guard": "reject",
"guard_reasons": [
- "raised the score by degrading the rest of the frame: flattened (0.55x), desaturated (0.30x), blurred (0.49x)"
+ "attention score did not improve"
],
"improved": false,
- "n_directives": 8,
+ "n_directives": 7,
"variant_heatmap": "replay/apple/step3-heat.png"
+ },
+ {
+ "step": 4,
+ "directive": "remove the blurry structure reflection completely and fill its area with what would naturally be behind it. Keep the Apple ad, product, logo and slogan exactly as they are",
+ "variant_png": "replay/apple/step4.jpg",
+ "current_score": 0.7732,
+ "new_score": 0.7748,
+ "delta": 0.0016,
+ "target_salience_before": 0.7594,
+ "target_salience_after": 0.7663,
+ "judge": 0.2,
+ "judge_reason": "While the image is visually interesting and highlights the iPhone, it lacks the typical polish and specific aesthetic of an official Apple ad. Apple's ads usually feature hyper-realistic, aspirational photography with meticulous attention to detail, often showing the latest model in pristine conditions or iconic settings. This image has a somewhat 'stock photo' feel due to the common 'floating object' trope, and the presence of notification badges on the app icons (e.g., messages, App Store) is something Apple often avoids in hero shots to maintain a clean, aspirational screen display. The model also appears to be an older iPhone X, rather than a current flagship typically featured in major campaigns. It wouldn't likely run as a real Apple ad due to these subtle but significant deviations from their brand guidelines.",
+ "vetoed": true,
+ "guard": "reject",
+ "guard_reasons": [
+ "attention score did not improve"
+ ],
+ "improved": false,
+ "n_directives": 8,
+ "variant_heatmap": "replay/apple/step4-heat.png"
}
]
\ No newline at end of file
diff --git a/frontend/public/replay/coca-cola/predict.json b/frontend/public/replay/coca-cola/predict.json
index d67a37a..59f22f2 100644
--- a/frontend/public/replay/coca-cola/predict.json
+++ b/frontend/public/replay/coca-cola/predict.json
@@ -45,7 +45,7 @@
0.2
],
"share": 0.073,
- "desc": "Wet soda can top"
+ "desc": "Coca-Cola can top"
},
{
"region": [
@@ -55,7 +55,7 @@
0.2
],
"share": 0.11,
- "desc": "Wet soda can body"
+ "desc": "Coca-Cola can side"
}
]
}
\ No newline at end of file
diff --git a/frontend/public/replay/coca-cola/step0-heat.png b/frontend/public/replay/coca-cola/step0-heat.png
index 0ad0447..173cc5f 100644
Binary files a/frontend/public/replay/coca-cola/step0-heat.png and b/frontend/public/replay/coca-cola/step0-heat.png differ
diff --git a/frontend/public/replay/coca-cola/step0.jpg b/frontend/public/replay/coca-cola/step0.jpg
index d933982..8266e3a 100644
Binary files a/frontend/public/replay/coca-cola/step0.jpg and b/frontend/public/replay/coca-cola/step0.jpg differ
diff --git a/frontend/public/replay/coca-cola/step1-heat.png b/frontend/public/replay/coca-cola/step1-heat.png
index 6e2f61d..dbb4e39 100644
Binary files a/frontend/public/replay/coca-cola/step1-heat.png and b/frontend/public/replay/coca-cola/step1-heat.png differ
diff --git a/frontend/public/replay/coca-cola/step1.jpg b/frontend/public/replay/coca-cola/step1.jpg
index 584f1fa..13e4418 100644
Binary files a/frontend/public/replay/coca-cola/step1.jpg and b/frontend/public/replay/coca-cola/step1.jpg differ
diff --git a/frontend/public/replay/coca-cola/step2-heat.png b/frontend/public/replay/coca-cola/step2-heat.png
index 4fb22e2..12ab0db 100644
Binary files a/frontend/public/replay/coca-cola/step2-heat.png and b/frontend/public/replay/coca-cola/step2-heat.png differ
diff --git a/frontend/public/replay/coca-cola/step2.jpg b/frontend/public/replay/coca-cola/step2.jpg
index ac8ed34..139e5f0 100644
Binary files a/frontend/public/replay/coca-cola/step2.jpg and b/frontend/public/replay/coca-cola/step2.jpg differ
diff --git a/frontend/public/replay/coca-cola/step3-heat.png b/frontend/public/replay/coca-cola/step3-heat.png
index 6459b69..c4828b0 100644
Binary files a/frontend/public/replay/coca-cola/step3-heat.png and b/frontend/public/replay/coca-cola/step3-heat.png differ
diff --git a/frontend/public/replay/coca-cola/step3.jpg b/frontend/public/replay/coca-cola/step3.jpg
index 330f8ea..e18cc59 100644
Binary files a/frontend/public/replay/coca-cola/step3.jpg and b/frontend/public/replay/coca-cola/step3.jpg differ
diff --git a/frontend/public/replay/coca-cola/step4-heat.png b/frontend/public/replay/coca-cola/step4-heat.png
new file mode 100644
index 0000000..1b54f6c
Binary files /dev/null and b/frontend/public/replay/coca-cola/step4-heat.png differ
diff --git a/frontend/public/replay/coca-cola/step4.jpg b/frontend/public/replay/coca-cola/step4.jpg
new file mode 100644
index 0000000..4f86a3e
Binary files /dev/null and b/frontend/public/replay/coca-cola/step4.jpg differ
diff --git a/frontend/public/replay/coca-cola/steps.json b/frontend/public/replay/coca-cola/steps.json
index 66e6ec0..f58aa86 100644
--- a/frontend/public/replay/coca-cola/steps.json
+++ b/frontend/public/replay/coca-cola/steps.json
@@ -1,82 +1,62 @@
[
{
"step": 0,
- "directive": "make SEVERAL coordinated changes at once to turn this into a polished campaign: remove the clutter and any objects, hands, props or stray text crowding or blocking the Coca-Cola logo and product; reframe it head-on and enlarge it as the clear hero; clean and simplify the background; and add a bold, legible on-brand headline, call-to-action and wordmark on the Coca-Cola logo and product",
+ "directive": "remove the Wet Coca-Cola can completely and fill its area with what would naturally be behind it. Keep the Coca-Cola ad, product, logo and slogan exactly as they are",
"variant_png": "replay/coca-cola/step0.jpg",
"current_score": 0.8026,
- "new_score": 0.5641,
- "delta": -0.2385,
+ "new_score": 0.8014,
+ "delta": -0.0012,
"target_salience_before": 0.8585,
- "target_salience_after": 0.2733,
- "judge": 0.98,
- "judge_reason": "This Coca-Cola ad creative is exceptionally high quality and demonstrates outstanding brand-fit. The photography of the can is professional, showcasing the product with appealing condensation that conveys extreme coldness and refreshment. The vibrant red background is the iconic Coca-Cola color, instantly recognizable. Both taglines, 'REFRESH YOUR MOMENT' and especially 'TASTE THE FEELING.' (which is Coca-Cola's actual global marketing tagline since 2016), are perfectly aligned with the brand's messaging of refreshment, experience, and emotional connection. The composition is clean, direct, and impactful, making the product the clear hero. This creative would absolutely run as a real ad; it embodies the quintessential Coca-Cola brand aesthetic and message.",
+ "target_salience_after": 0.8524,
+ "judge": 0.95,
+ "judge_reason": "This creative is exceptionally high quality and a perfect brand-fit for Coca-Cola. The image is a professional product shot, impeccably lit to highlight the condensation and metallic sheen of the can, strongly conveying refreshment and a premium feel. The use of the iconic Coca-Cola red as a clean, simple background is classic brand reinforcement. The composition is clean, focused, and directly showcases the product and its iconic logo. This style of direct, high-quality product photography is a staple of Coca-Cola's advertising and would absolutely run as a real ad across various media, from print to digital.",
"vetoed": false,
"guard": "reject",
"guard_reasons": [
"attention score did not improve"
],
"improved": false,
- "n_directives": 7,
+ "n_directives": 3,
"variant_heatmap": "replay/coca-cola/step0-heat.png"
},
{
"step": 1,
- "directive": "add a bold, legible, on-brand headline and call-to-action and strengthen the logo/wordmark right at the Coca-Cola logo and product as a strong, high-contrast focal point",
+ "directive": "remove the Coca-Cola can completely and fill its area with what would naturally be behind it. Keep the Coca-Cola ad, product, logo and slogan exactly as they are",
"variant_png": "replay/coca-cola/step1.jpg",
"current_score": 0.8026,
- "new_score": 0.5539,
- "delta": -0.2487,
+ "new_score": 0.8078,
+ "delta": 0.0052,
"target_salience_before": 0.8585,
- "target_salience_after": 0.2623,
- "judge": 0.9,
- "judge_reason": "This creative is highly effective and demonstrates strong brand fit for Coca-Cola. The use of the iconic red background, a perfectly photographed cold can with condensation, and the prominent display of the Coca-Cola logo immediately establish brand identity. The headline 'REFRESH YOUR MOMENT' aligns perfectly with Coca-Cola's core messaging around refreshment and experience. The tagline 'TASTE THE FEELING. BUY NOW.' directly incorporates a well-known global Coca-Cola campaign tagline ('Taste the Feeling') and a clear call to action. While the headline font isn't the classic Coca-Cola Spencerian script, it's a stylistic choice that doesn't detract from the brand's essence, and the overall composition is clean, direct, and professional. This ad would very likely run as a real ad in various formats (digital, print, in-store) due to its clarity, strong branding, and direct appeal.",
+ "target_salience_after": 0.8878,
+ "judge": 1.0,
+ "judge_reason": "This ad creative is exceptionally high quality and a perfect brand-fit for Coca-Cola. The image features impeccable lighting, showing the can with realistic condensation that evokes a sense of cold refreshment. The iconic red background is a signature brand element, making the product instantly recognizable and visually appealing. The composition is clean, professional, and directly highlights the product, which is a common and effective strategy for a brand like Coca-Cola. It absolutely would run as a real ad, likely as a hero shot in print, digital, or even billboard campaigns due to its strong visual impact and brand resonance.",
"vetoed": false,
- "guard": "reject",
+ "guard": "accept",
"guard_reasons": [
- "attention score did not improve"
+ "perceptible, localized/justified, and target salience improved"
],
- "improved": false,
- "n_directives": 7,
+ "improved": true,
+ "n_directives": 4,
"variant_heatmap": "replay/coca-cola/step1-heat.png"
},
{
"step": 2,
- "directive": "clearly enlarge, brighten and sharpen the Coca-Cola logo and product so it becomes the single biggest, boldest focal element while gently dimming and de-cluttering the surroundings \u2014 a polished, real ad",
+ "directive": "make the surface of the Coca-Cola product a little crisper and cleaner with clean highlights, without adding, changing or removing any text or logo",
"variant_png": "replay/coca-cola/step2.jpg",
- "current_score": 0.8026,
- "new_score": 0.6652,
- "delta": -0.1374,
- "target_salience_before": 0.8585,
- "target_salience_after": 0.4196,
- "judge": 1.0,
- "judge_reason": "This Coca-Cola ad creative is exceptionally high quality and a perfect brand fit. The image features a perfectly rendered, condensation-laden Coca-Cola can against an iconic red background, immediately evoking refreshment. The use of the official global campaign tagline, 'TASTE THE FEELING,' further solidifies its authenticity and brand alignment. The minimalist yet powerful composition is characteristic of Coca-Cola's advertising, making it highly effective and absolutely suitable to run as a real ad.",
+ "current_score": 0.8078,
+ "new_score": 0.8049,
+ "delta": -0.0029,
+ "target_salience_before": 0.8878,
+ "target_salience_after": 0.8716,
+ "judge": 0.95,
+ "judge_reason": "This Coca-Cola ad creative is excellent in both quality and brand-fit. The photography is crisp, well-lit, and the condensation on the can effectively communicates coolness and refreshment. The vibrant red background is iconic to Coca-Cola, instantly recognizable, and creates a strong brand presence. The composition is clean and focused solely on the product, which is a common and effective strategy for product-centric advertising. This creative absolutely embodies the Coca-Cola brand aesthetic and would very likely run as a real ad across various media, from digital banners to print ads or retail displays.",
"vetoed": false,
"guard": "reject",
"guard_reasons": [
"attention score did not improve"
],
"improved": false,
- "n_directives": 7,
+ "n_directives": 3,
"variant_heatmap": "replay/coca-cola/step2-heat.png"
- },
- {
- "step": 3,
- "directive": "remove or clean away any objects, hands, props or background obstructions that crowd or block the Coca-Cola logo and product so the product is fully visible, unobstructed, and the clear hero of the shot",
- "variant_png": "replay/coca-cola/step3.jpg",
- "current_score": 0.8026,
- "new_score": 0.6691,
- "delta": -0.1335,
- "target_salience_before": 0.8585,
- "target_salience_after": 0.427,
- "judge": 1.0,
- "judge_reason": "This creative is of excellent quality and perfectly aligns with the Coca-Cola brand. The vibrant red background, the perfectly chilled can with condensation, and the iconic 'Taste the Feeling' tagline are all quintessential Coca-Cola. The photography/rendering is professional and appealing, making the product look refreshing. The minimalist composition focuses entirely on the product and brand message. This would undoubtedly run as a real ad, fitting seamlessly into their established advertising aesthetic.",
- "vetoed": false,
- "guard": "reject",
- "guard_reasons": [
- "attention score did not improve"
- ],
- "improved": false,
- "n_directives": 7,
- "variant_heatmap": "replay/coca-cola/step3-heat.png"
}
]
\ No newline at end of file
diff --git a/frontend/public/replay/mcdonalds/step0-heat.png b/frontend/public/replay/mcdonalds/step0-heat.png
index 077d1a2..15d8175 100644
Binary files a/frontend/public/replay/mcdonalds/step0-heat.png and b/frontend/public/replay/mcdonalds/step0-heat.png differ
diff --git a/frontend/public/replay/mcdonalds/step0.jpg b/frontend/public/replay/mcdonalds/step0.jpg
index bfe5d37..e7f9f9d 100644
Binary files a/frontend/public/replay/mcdonalds/step0.jpg and b/frontend/public/replay/mcdonalds/step0.jpg differ
diff --git a/frontend/public/replay/mcdonalds/steps.json b/frontend/public/replay/mcdonalds/steps.json
index 0623768..d5c4708 100644
--- a/frontend/public/replay/mcdonalds/steps.json
+++ b/frontend/public/replay/mcdonalds/steps.json
@@ -1,82 +1,22 @@
[
{
"step": 0,
- "directive": "make SEVERAL coordinated changes at once to turn this into a polished campaign: remove the clutter and any objects, hands, props or stray text crowding or blocking the McDonald's logo and product; reframe it head-on and enlarge it as the clear hero; clean and simplify the background; and add a bold, legible on-brand headline, call-to-action and wordmark on the McDonald's logo and product",
+ "directive": "make the McDonald's product itself a little crisper and cleaner: sharper label and logo edges and clean highlights, same color, same size, same place",
"variant_png": "replay/mcdonalds/step0.jpg",
"current_score": 0.8383,
- "new_score": 0.7213,
- "delta": -0.117,
+ "new_score": 0.8386,
+ "delta": 0.0003,
"target_salience_before": 0.8893,
- "target_salience_after": 0.4438,
- "judge": 0.95,
- "judge_reason": "This is a high-quality, brand-fit creative that would absolutely run as a real ad for McDonald's. The visual of the illuminated Golden Arches against a dark, night sky immediately sets the scene for 'late night.' The copy 'LATE NIGHT CRAVINGS? FIND YOURS. ALWAYS OPEN' is concise, directly addresses a common consumer need, and highlights a key brand strength (convenience/extended hours). The use of the iconic McDonald's colors (yellow and red) is strong, and the overall composition is impactful and clear. The Japanese text on the sign adds a subtle touch of global authenticity without detracting from the universal message. It's a very effective ad.",
- "vetoed": false,
- "guard": "reject",
- "guard_reasons": [
- "attention score did not improve"
- ],
- "improved": false,
- "n_directives": 6,
- "variant_heatmap": "replay/mcdonalds/step0-heat.png"
- },
- {
- "step": 1,
- "directive": "add a bold, legible, on-brand headline and call-to-action and strengthen the logo/wordmark right at the McDonald's logo and product as a strong, high-contrast focal point",
- "variant_png": "replay/mcdonalds/step1.jpg",
- "current_score": 0.8383,
- "new_score": 0.7412,
- "delta": -0.0971,
- "target_salience_before": 0.8893,
- "target_salience_after": 0.4911,
- "judge": 0.85,
- "judge_reason": "This creative has strong brand recognition and a clear message. The iconic golden arches and McDonald's logo (with Japanese 'drive-thru' text) are instantly identifiable. The 'ALWAYS OPEN' headline perfectly aligns with McDonald's messaging of convenience and availability, especially for late-night or early-morning customers. 'ORDER NOW' provides a direct call to action. The use of McDonald's brand colors (yellow, red, against a dark background) makes the sign pop. While the photographic quality of the sign itself appears somewhat raw or weathered (which some campaigns might embrace for authenticity, others might polish), and the 'ALWAYS OPEN' text is digitally overlaid, the core elements are effective and on-brand. It's highly likely this could run as a real ad, particularly for digital channels, social media, or localized campaigns emphasizing convenience.",
- "vetoed": false,
- "guard": "reject",
- "guard_reasons": [
- "attention score did not improve"
- ],
- "improved": false,
- "n_directives": 6,
- "variant_heatmap": "replay/mcdonalds/step1-heat.png"
- },
- {
- "step": 2,
- "directive": "clearly enlarge, brighten and sharpen the McDonald's logo and product so it becomes the single biggest, boldest focal element while gently dimming and de-cluttering the surroundings \u2014 a polished, real ad",
- "variant_png": "replay/mcdonalds/step2.jpg",
- "current_score": 0.8383,
- "new_score": 0.7827,
- "delta": -0.0556,
- "target_salience_before": 0.8893,
- "target_salience_after": 0.6177,
+ "target_salience_after": 0.8908,
"judge": 0.9,
- "judge_reason": "This creative has excellent brand fit and high quality. The iconic McDonald's golden arches and logotype are instantly recognizable, even in this stark, nighttime setting. The clear 'DRIVE-THRU' messaging in both Japanese and English effectively communicates a key service. The dark background makes the illuminated sign pop, giving it a strong visual impact that would work well across various ad placements (digital billboards, social media, print). McDonald's frequently uses its iconic signage in advertising to reinforce brand presence and highlight convenience, making this a very plausible and effective real ad.",
+ "judge_reason": "This creative is high quality and has excellent brand-fit. The image leverages McDonald's most iconic elements: the glowing Golden Arches and the red sign with the brand name. The dark background makes the signage pop, creating a strong visual impact that is instantly recognizable. The inclusion of '\u30c9\u30e9\u30a4\u30d6\u30b9\u30eb\u30fc' (Drive-Thru) clearly communicates a key service. McDonald's frequently uses images of its iconic signs, especially at night, to evoke feelings of convenience, availability, and a welcoming presence. This image could very easily run as a real ad, for instance, in campaigns highlighting late-night service, drive-thru convenience, or general brand presence. The simplicity and strong focus on brand identity make it highly effective.",
"vetoed": false,
"guard": "reject",
"guard_reasons": [
"attention score did not improve"
],
"improved": false,
- "n_directives": 6,
- "variant_heatmap": "replay/mcdonalds/step2-heat.png"
- },
- {
- "step": 3,
- "directive": "remove or clean away any objects, hands, props or background obstructions that crowd or block the McDonald's logo and product so the product is fully visible, unobstructed, and the clear hero of the shot",
- "variant_png": "replay/mcdonalds/step3.jpg",
- "current_score": 0.8383,
- "new_score": 0.8042,
- "delta": -0.0341,
- "target_salience_before": 0.8893,
- "target_salience_after": 0.7045,
- "judge": 0.9,
- "judge_reason": "This ad creative is very high quality and fits the McDonald's brand perfectly. The image is clean, well-lit, and immediately recognizable as McDonald's due to the iconic Golden Arches and brand colors (red and yellow). The message 'OPEN LATE' is clear, concise, and directly relevant to a common customer need, especially for late-night cravings. The inclusion of '\u30c9\u30e9\u30a4\u30d6\u30b9\u30eb\u30fc' (Drive-thru) further emphasizes convenience, a key aspect of McDonald's service. This creative effectively communicates a valuable service offering using strong brand elements and would absolutely run as a real ad, particularly in markets like Japan where this sign style is common.",
- "vetoed": false,
- "guard": "reject",
- "guard_reasons": [
- "attention score did not improve"
- ],
- "improved": false,
- "n_directives": 6,
- "variant_heatmap": "replay/mcdonalds/step3-heat.png"
+ "n_directives": 1,
+ "variant_heatmap": "replay/mcdonalds/step0-heat.png"
}
]
\ No newline at end of file
diff --git a/frontend/public/replay/nike/predict.json b/frontend/public/replay/nike/predict.json
index 3e3ce1a..5df2cc4 100644
--- a/frontend/public/replay/nike/predict.json
+++ b/frontend/public/replay/nike/predict.json
@@ -45,7 +45,8 @@
0.2
],
"share": 0.36,
- "desc": "Subway sign"
+ "desc": "Red Subway sign",
+ "own": false
},
{
"region": [
@@ -55,7 +56,8 @@
0.2
],
"share": 0.074,
- "desc": "No Turns sign"
+ "desc": "No Turns sign",
+ "own": false
}
]
}
\ No newline at end of file
diff --git a/frontend/public/replay/nike/step0-heat.png b/frontend/public/replay/nike/step0-heat.png
index 2ed2250..b561a49 100644
Binary files a/frontend/public/replay/nike/step0-heat.png and b/frontend/public/replay/nike/step0-heat.png differ
diff --git a/frontend/public/replay/nike/step0.jpg b/frontend/public/replay/nike/step0.jpg
index fd5835a..c414934 100644
Binary files a/frontend/public/replay/nike/step0.jpg and b/frontend/public/replay/nike/step0.jpg differ
diff --git a/frontend/public/replay/nike/step1-heat.png b/frontend/public/replay/nike/step1-heat.png
index ae6bf09..044f7ad 100644
Binary files a/frontend/public/replay/nike/step1-heat.png and b/frontend/public/replay/nike/step1-heat.png differ
diff --git a/frontend/public/replay/nike/step1.jpg b/frontend/public/replay/nike/step1.jpg
index 37a80a6..5512890 100644
Binary files a/frontend/public/replay/nike/step1.jpg and b/frontend/public/replay/nike/step1.jpg differ
diff --git a/frontend/public/replay/nike/step2-heat.png b/frontend/public/replay/nike/step2-heat.png
index e503b1b..8feba99 100644
Binary files a/frontend/public/replay/nike/step2-heat.png and b/frontend/public/replay/nike/step2-heat.png differ
diff --git a/frontend/public/replay/nike/step2.jpg b/frontend/public/replay/nike/step2.jpg
index 23601b7..5a895e5 100644
Binary files a/frontend/public/replay/nike/step2.jpg and b/frontend/public/replay/nike/step2.jpg differ
diff --git a/frontend/public/replay/nike/step3-heat.png b/frontend/public/replay/nike/step3-heat.png
index b430160..59bdf4f 100644
Binary files a/frontend/public/replay/nike/step3-heat.png and b/frontend/public/replay/nike/step3-heat.png differ
diff --git a/frontend/public/replay/nike/step3.jpg b/frontend/public/replay/nike/step3.jpg
index ea1cb59..5665d99 100644
Binary files a/frontend/public/replay/nike/step3.jpg and b/frontend/public/replay/nike/step3.jpg differ
diff --git a/frontend/public/replay/nike/step4-heat.png b/frontend/public/replay/nike/step4-heat.png
new file mode 100644
index 0000000..4ea979b
Binary files /dev/null and b/frontend/public/replay/nike/step4-heat.png differ
diff --git a/frontend/public/replay/nike/step4.jpg b/frontend/public/replay/nike/step4.jpg
new file mode 100644
index 0000000..57f40fa
Binary files /dev/null and b/frontend/public/replay/nike/step4.jpg differ
diff --git a/frontend/public/replay/nike/steps.json b/frontend/public/replay/nike/steps.json
index 90c8672..97e340c 100644
--- a/frontend/public/replay/nike/steps.json
+++ b/frontend/public/replay/nike/steps.json
@@ -1,82 +1,102 @@
[
{
"step": 0,
- "directive": "make SEVERAL coordinated changes at once to turn this into a polished campaign: remove the clutter and any objects, hands, props or stray text crowding or blocking the Nike logo and product; reframe it head-on and enlarge it as the clear hero; clean and simplify the background; and add a bold, legible on-brand headline, call-to-action and wordmark on the Nike logo and product",
+ "directive": "remove the Storefront signs completely and fill its area with what would naturally be behind it. Keep the Nike ad, product, logo and slogan exactly as they are",
"variant_png": "replay/nike/step0.jpg",
"current_score": 0.5922,
- "new_score": 0.446,
- "delta": -0.1462,
+ "new_score": 0.7125,
+ "delta": 0.1203,
"target_salience_before": 0.196,
- "target_salience_after": 0.1086,
- "judge": 1.0,
- "judge_reason": "This Nike ad creative is exceptionally high quality and a perfect brand fit. It features a dynamic action shot of a recognizable athlete (Odell Beckham Jr.) that visually conveys the 'DEFY GRAVITY' message. The use of black and white photography with a single, strategic color pop (the orange cleat) is a classic and effective advertising technique often employed by Nike to highlight product and create visual interest. The aspirational headline 'DEFY GRAVITY' combined with Nike's iconic 'Just do it.' slogan strongly reinforces the brand's core values of performance, aspiration, and pushing human limits. This creative is impactful, visually compelling, and perfectly aligned with Nike's established brand identity, making it highly likely to run as a real ad.",
+ "target_salience_after": 0.3344,
+ "judge": 0.98,
+ "judge_reason": "This Nike ad creative demonstrates exceptional quality and brand-fit. Featuring Odell Beckham Jr., a high-profile athlete, in a dynamic black-and-white action shot immediately captures attention. The ad expertly blends a motivational secondary slogan, 'Don't stop 'til you get enough,' with Nike's iconic 'Just do it.' tagline. This combination reinforces Nike's core values of perseverance, achievement, and pushing limits. The visual aesthetic is clean, powerful, and consistent with Nike's premium branding. It's highly effective for a large-format display like a billboard, communicating a strong message quickly. This ad would absolutely run as a real Nike ad, aligning perfectly with their history of athlete endorsements, inspirational messaging, and distinctive visual style.",
"vetoed": false,
- "guard": "reject",
+ "guard": "accept",
"guard_reasons": [
- "attention score did not improve"
+ "perceptible, localized/justified, and target salience improved"
],
- "improved": false,
- "n_directives": 8,
+ "improved": true,
+ "n_directives": 4,
"variant_heatmap": "replay/nike/step0-heat.png"
},
{
"step": 1,
- "directive": "add a bold, legible, on-brand headline and call-to-action and strengthen the logo/wordmark right at the Nike logo and product as a strong, high-contrast focal point",
+ "directive": "remove the No Turns sign completely and fill its area with what would naturally be behind it. Keep the Nike ad, product, logo and slogan exactly as they are",
"variant_png": "replay/nike/step1.jpg",
- "current_score": 0.5922,
- "new_score": 0.4746,
- "delta": -0.1176,
- "target_salience_before": 0.196,
- "target_salience_after": 0.1219,
+ "current_score": 0.7125,
+ "new_score": 0.7458,
+ "delta": 0.0333,
+ "target_salience_before": 0.3344,
+ "target_salience_after": 0.3959,
"judge": 1.0,
- "judge_reason": "This Nike ad creative is exceptionally high quality and a perfect fit for the brand. It features a prominent Nike-sponsored athlete (Odell Beckham Jr.), utilizes Nike's iconic 'Just Do It.' tagline along with the aspirational 'Unleash Your Greatness' slogan, and presents a powerful, dynamic image in a classic black and white style frequently employed by Nike for impactful campaigns. The clear call to action 'SHOP NOW' makes it suitable for a real-world advertisement, especially on a prominent digital billboard like the one depicted. This creative strongly aligns with Nike's brand identity of athletic excellence, motivation, and empowerment.",
+ "judge_reason": "This ad creative is exceptionally high quality and perfectly aligns with Nike's brand. It features a prominent athlete (Odell Beckham Jr.) in a dynamic, black-and-white image, conveying intensity and dedication. The motivational message 'Don't stop 'til you get enough' is consistent with Nike's 'Just Do It' ethos, which is prominently displayed as the iconic tagline at the bottom. The visual style, athletic focus, and inspirational messaging are all hallmarks of Nike's global advertising campaigns. This ad not only would run as a real ad, but it exemplifies the brand's established and successful advertising strategy.",
"vetoed": false,
- "guard": "reject",
+ "guard": "accept",
"guard_reasons": [
- "attention score did not improve"
+ "perceptible, localized/justified, and target salience improved"
],
- "improved": false,
- "n_directives": 8,
+ "improved": true,
+ "n_directives": 5,
"variant_heatmap": "replay/nike/step1-heat.png"
},
{
"step": 2,
- "directive": "clearly enlarge, brighten and sharpen the Nike logo and product so it becomes the single biggest, boldest focal element while gently dimming and de-cluttering the surroundings \u2014 a polished, real ad",
+ "directive": "remove the Swarovski logo completely and fill its area with what would naturally be behind it. Keep the Nike ad, product, logo and slogan exactly as they are",
"variant_png": "replay/nike/step2.jpg",
- "current_score": 0.5922,
- "new_score": 0.5364,
- "delta": -0.0558,
- "target_salience_before": 0.196,
- "target_salience_after": 0.1561,
- "judge": 0.95,
- "judge_reason": "This Nike ad creative is exceptionally high quality and a perfect fit for the brand. It leverages Nike's iconic 'Just do it.' slogan prominently. The use of a recognizable athlete (Odell Beckham Jr. in a dynamic action shot) is characteristic of Nike's advertising strategy. The black and white aesthetic with a grainy texture creates a classic, powerful, and gritty feel that aligns perfectly with Nike's brand identity of dedication, perseverance, and athletic excellence. The secondary slogan, 'Don't stop 'til you get enough,' further reinforces this message. The repetition of 'Just do it.' (main and smaller at the bottom with a subtle swoosh) ensures strong brand recall. This ad would absolutely run as a real Nike ad, as it encapsulates all the hallmarks of their successful campaigns.",
+ "current_score": 0.7458,
+ "new_score": 0.7552,
+ "delta": 0.0094,
+ "target_salience_before": 0.3959,
+ "target_salience_after": 0.4163,
+ "judge": 1.0,
+ "judge_reason": "This Nike ad creative demonstrates exceptional quality and brand-fit. It features a prominent athlete (Odell Beckham Jr.) in a dynamic, action-oriented shot, which is a hallmark of Nike's marketing strategy. The black and white aesthetic adds a powerful, timeless feel. The primary message, 'Don't stop 'til you get enough,' is highly motivational and perfectly aligns with Nike's brand ethos of perseverance, pushing limits, and athletic achievement. The inclusion of the iconic 'Just do it.' tagline reinforces brand identity instantly and effectively. This ad is a strong, cohesive, and impactful piece of creative that would undoubtedly run as a real Nike campaign, likely on major outdoor placements like the one shown.",
"vetoed": false,
- "guard": "reject",
+ "guard": "accept",
"guard_reasons": [
- "attention score did not improve"
+ "perceptible, localized/justified, and target salience improved"
],
- "improved": false,
- "n_directives": 8,
+ "improved": true,
+ "n_directives": 6,
"variant_heatmap": "replay/nike/step2-heat.png"
},
{
"step": 3,
- "directive": "remove or clean away any objects, hands, props or background obstructions that crowd or block the Nike logo and product so the product is fully visible, unobstructed, and the clear hero of the shot",
+ "directive": "remove the A$AP Ferg poster completely and fill its area with what would naturally be behind it. Keep the Nike ad, product, logo and slogan exactly as they are",
"variant_png": "replay/nike/step3.jpg",
- "current_score": 0.5922,
- "new_score": 0.5959,
- "delta": 0.0037,
- "target_salience_before": 0.196,
- "target_salience_after": 0.199,
- "judge": 1.0,
- "judge_reason": "This Nike ad creative is of extremely high quality and an impeccable brand fit. It features Odell Beckham Jr., a high-profile Nike athlete known for his spectacular plays, captured in a dynamic, aspirational pose. The black and white aesthetic with the prominently highlighted white Nike cleats creates a strong visual impact, drawing attention to the product while maintaining an iconic feel. The message, 'Don't stop 'til you get enough,' perfectly encapsulates Nike's motivational, relentless pursuit of greatness ethos, which is further cemented by their iconic 'Just do it.' slogan. This ad perfectly aligns with Nike's brand identity of inspiring athletes to push their limits and achieve excellence. It absolutely would run as a real Nike ad, likely as part of a major campaign during OBJ's prime.",
+ "current_score": 0.757,
+ "new_score": 0.7705,
+ "delta": 0.0135,
+ "target_salience_before": 0.4203,
+ "target_salience_after": 0.4532,
+ "judge": 0.95,
+ "judge_reason": "This Nike ad creative demonstrates excellent quality and strong brand-fit. The use of a prominent athlete like Odell Beckham Jr. (visible by 'Beckham Jr. 13'), captured in a dynamic, action-oriented black and white photograph, is a classic and effective Nike strategy. The motivational secondary tagline, 'Don't stop 'til you get enough,' perfectly aligns with Nike's ethos of perseverance, pushing limits, and achieving greatness. This message is then powerfully reinforced by the iconic 'Just do it.' slogan. The aesthetic is clean, impactful, and instantly recognizable as Nike. The choice of a black and white image adds a timeless, powerful, and focused feel, ensuring the message stands out. This ad would absolutely run as a real Nike ad, as it encapsulates their brand values, visual identity, and messaging strategy effectively.",
"vetoed": false,
"guard": "accept",
"guard_reasons": [
"perceptible, localized/justified, and target salience improved"
],
"improved": true,
- "n_directives": 8,
+ "n_directives": 6,
"variant_heatmap": "replay/nike/step3-heat.png"
+ },
+ {
+ "step": 4,
+ "directive": "remove the Street signs completely and fill its area with what would naturally be behind it. Keep the Nike ad, product, logo and slogan exactly as they are",
+ "variant_png": "replay/nike/step4.jpg",
+ "current_score": 0.7705,
+ "new_score": 0.7732,
+ "delta": 0.0027,
+ "target_salience_before": 0.4532,
+ "target_salience_after": 0.4602,
+ "judge": 0.95,
+ "judge_reason": "This Nike ad creative is of extremely high quality and exhibits an excellent brand-fit. It features a prominent athlete (Odell Beckham Jr.) in a dynamic, black-and-white image, which is a classic and effective style for Nike. The motivational message, 'Don't stop 'til you get enough,' perfectly aligns with Nike's ethos of perseverance and pushing limits. The inclusion of the iconic 'Just do it.' slogan leaves no doubt about the brand. The overall composition is strong, visually appealing, and delivers a clear, inspiring message. This creative is highly plausible as a real Nike ad; it strongly reflects their advertising strategy, aesthetic, and core brand values.",
+ "vetoed": false,
+ "guard": "reject",
+ "guard_reasons": [
+ "attention score did not improve"
+ ],
+ "improved": false,
+ "n_directives": 7,
+ "variant_heatmap": "replay/nike/step4-heat.png"
}
]
\ No newline at end of file
diff --git a/frontend/public/replay/pepsi/step0-heat.png b/frontend/public/replay/pepsi/step0-heat.png
index 451afd0..77e774e 100644
Binary files a/frontend/public/replay/pepsi/step0-heat.png and b/frontend/public/replay/pepsi/step0-heat.png differ
diff --git a/frontend/public/replay/pepsi/step0.jpg b/frontend/public/replay/pepsi/step0.jpg
index 92e41c8..95217d1 100644
Binary files a/frontend/public/replay/pepsi/step0.jpg and b/frontend/public/replay/pepsi/step0.jpg differ
diff --git a/frontend/public/replay/pepsi/steps.json b/frontend/public/replay/pepsi/steps.json
index 18122f3..b4efb55 100644
--- a/frontend/public/replay/pepsi/steps.json
+++ b/frontend/public/replay/pepsi/steps.json
@@ -1,82 +1,22 @@
[
{
"step": 0,
- "directive": "make SEVERAL coordinated changes at once to turn this into a polished campaign: remove the clutter and any objects, hands, props or stray text crowding or blocking the Pepsi logo and product; reframe it head-on and enlarge it as the clear hero; clean and simplify the background; and add a bold, legible on-brand headline, call-to-action and wordmark on the Pepsi logo and product",
+ "directive": "make the Pepsi product itself a little crisper and cleaner: sharper label and logo edges and clean highlights, same color, same size, same place",
"variant_png": "replay/pepsi/step0.jpg",
"current_score": 0.8554,
- "new_score": 0.5795,
- "delta": -0.2759,
+ "new_score": 0.8554,
+ "delta": 0.0,
"target_salience_before": 0.9018,
- "target_salience_after": 0.2102,
- "judge": 0.4,
- "judge_reason": "The ad creative is very basic and generic. While it uses Pepsi's brand colors and features the product, the photography of the can is flat, lacking the 'cold' or 'refreshing' visual cues (like condensation or vibrant lighting) typically used in beverage advertising to create desire. The typography is simple and functional but not distinctive, and the overall composition lacks creativity or a unique selling proposition beyond a very generic 'refresh your day.' It feels more like an internal draft or a low-budget social media filler than a polished, professionally produced ad from a global brand like Pepsi. It would likely be rejected or require significant improvements in photography and creative direction to run as a real ad.",
+ "target_salience_after": 0.9017,
+ "judge": 0.35,
+ "judge_reason": "The ad clearly features a Pepsi can against a blue background, which is on-brand for color recognition. However, the overall creative quality falls short of what a major brand like Pepsi typically produces for its advertising campaigns. The lighting is flat and doesn't make the can appear vibrant, cold, or refreshing. The shadow beneath the can is somewhat harsh, and the composition, while clean, lacks the dynamic appeal, high production value, or aspirational context usually seen in Pepsi ads. It resembles a basic stock photo or a product shot for an internal catalog rather than polished ad creative intended to drive brand engagement or desire. It would likely only run as a very simple, functional placeholder or a low-budget banner, not as a primary ad.",
"vetoed": true,
"guard": "reject",
"guard_reasons": [
"attention score did not improve"
],
"improved": false,
- "n_directives": 6,
+ "n_directives": 1,
"variant_heatmap": "replay/pepsi/step0-heat.png"
- },
- {
- "step": 1,
- "directive": "add a bold, legible, on-brand headline and call-to-action and strengthen the logo/wordmark right at the Pepsi logo and product as a strong, high-contrast focal point",
- "variant_png": "replay/pepsi/step1.jpg",
- "current_score": 0.8554,
- "new_score": 0.5471,
- "delta": -0.3083,
- "target_salience_before": 0.9018,
- "target_salience_after": 0.1842,
- "judge": 0.5,
- "judge_reason": "This creative has good brand-fit due to its iconic Pepsi blue background, prominent display of the product, and use of plausible cola slogans like 'REFRESH YOUR WORLD' and 'TASTE THE BREAK.' However, the overall quality falls short of what a major global brand like Pepsi would typically produce. The photography is basic, lacking the dynamic lighting, crispness, and visual appeal usually seen in professional ad campaigns (e.g., no condensation, somewhat flat lighting). The composition is extremely simple, almost to the point of being bland, and the use of two distinct taglines at the top and bottom can feel disjointed rather than cohesive. While it effectively communicates the brand and core message, its execution quality suggests it's more of a concept or mock-up rather than a finished ad that would run as-is in a real campaign for Pepsi.",
- "vetoed": false,
- "guard": "reject",
- "guard_reasons": [
- "attention score did not improve"
- ],
- "improved": false,
- "n_directives": 6,
- "variant_heatmap": "replay/pepsi/step1-heat.png"
- },
- {
- "step": 2,
- "directive": "clearly enlarge, brighten and sharpen the Pepsi logo and product so it becomes the single biggest, boldest focal element while gently dimming and de-cluttering the surroundings \u2014 a polished, real ad",
- "variant_png": "replay/pepsi/step2.jpg",
- "current_score": 0.8554,
- "new_score": 0.7039,
- "delta": -0.1515,
- "target_salience_before": 0.9018,
- "target_salience_after": 0.3625,
- "judge": 0.65,
- "judge_reason": "The ad is clear, on-brand with the prominent Pepsi can and the signature blue color palette. The slogan 'TASTE THE REFRESHMENT' is direct and relevant to a beverage product. It's a clean, minimalist design that effectively communicates the product and a core benefit. However, the overall visual quality is somewhat flat; it lacks the dynamic lighting, condensation, or other visual cues often used in beverage advertising to truly evoke the feeling of 'refreshment' and make the product more enticing. It feels functional but lacks creative sparkle or high production value typically seen in leading Pepsi campaigns. It could definitely run as a real ad, especially for digital banners, social media posts, or tactical print, but it's not a particularly strong or memorable piece of advertising that would anchor a major campaign.",
- "vetoed": false,
- "guard": "reject",
- "guard_reasons": [
- "attention score did not improve"
- ],
- "improved": false,
- "n_directives": 6,
- "variant_heatmap": "replay/pepsi/step2-heat.png"
- },
- {
- "step": 3,
- "directive": "remove or clean away any objects, hands, props or background obstructions that crowd or block the Pepsi logo and product so the product is fully visible, unobstructed, and the clear hero of the shot",
- "variant_png": "replay/pepsi/step3.jpg",
- "current_score": 0.8554,
- "new_score": 0.6764,
- "delta": -0.179,
- "target_salience_before": 0.9018,
- "target_salience_after": 0.3188,
- "judge": 0.8,
- "judge_reason": "This Pepsi ad creative is simple, clean, and effective. The product is prominently featured against a vibrant blue background that perfectly aligns with Pepsi's brand colors. The photography of the can is good, and the text 'REFRESH YOUR WORLD' is legible and conveys a core brand benefit. It's a very direct and functional ad that fits the brand well in terms of color, product, and message. It would absolutely run as a real ad, especially in digital formats (social media, banner ads) where quick recognition and clear messaging are key. However, it scores slightly lower than a perfect 1.0 because it's quite basic and lacks the high-end creative flair, dynamic elements (like condensation, ice, or lifestyle context), or unique visual storytelling often seen in major, aspirational campaigns from a brand like Pepsi. It's a solid, safe, and brand-appropriate ad, but not groundbreaking.",
- "vetoed": false,
- "guard": "reject",
- "guard_reasons": [
- "attention score did not improve"
- ],
- "improved": false,
- "n_directives": 6,
- "variant_heatmap": "replay/pepsi/step3-heat.png"
}
]
\ No newline at end of file
diff --git a/frontend/public/replay/red-bull/predict.json b/frontend/public/replay/red-bull/predict.json
index 7405be3..1427184 100644
--- a/frontend/public/replay/red-bull/predict.json
+++ b/frontend/public/replay/red-bull/predict.json
@@ -45,7 +45,7 @@
0.2
],
"share": 0.094,
- "desc": "White phone back"
+ "desc": "White device back"
},
{
"region": [
@@ -55,7 +55,7 @@
0.2
],
"share": 0.055,
- "desc": "Smartphone screen"
+ "desc": "Black phone screen"
}
]
}
\ No newline at end of file
diff --git a/frontend/public/replay/red-bull/step0-heat.png b/frontend/public/replay/red-bull/step0-heat.png
index 845a093..97734de 100644
Binary files a/frontend/public/replay/red-bull/step0-heat.png and b/frontend/public/replay/red-bull/step0-heat.png differ
diff --git a/frontend/public/replay/red-bull/step0.jpg b/frontend/public/replay/red-bull/step0.jpg
index 5de9fd5..cf5597d 100644
Binary files a/frontend/public/replay/red-bull/step0.jpg and b/frontend/public/replay/red-bull/step0.jpg differ
diff --git a/frontend/public/replay/red-bull/step1-heat.png b/frontend/public/replay/red-bull/step1-heat.png
index d3eecf7..592ba72 100644
Binary files a/frontend/public/replay/red-bull/step1-heat.png and b/frontend/public/replay/red-bull/step1-heat.png differ
diff --git a/frontend/public/replay/red-bull/step1.jpg b/frontend/public/replay/red-bull/step1.jpg
index 117a80c..a24bb6e 100644
Binary files a/frontend/public/replay/red-bull/step1.jpg and b/frontend/public/replay/red-bull/step1.jpg differ
diff --git a/frontend/public/replay/red-bull/step2-heat.png b/frontend/public/replay/red-bull/step2-heat.png
index bdb9a5a..97734de 100644
Binary files a/frontend/public/replay/red-bull/step2-heat.png and b/frontend/public/replay/red-bull/step2-heat.png differ
diff --git a/frontend/public/replay/red-bull/step2.jpg b/frontend/public/replay/red-bull/step2.jpg
index a4b0681..cf5597d 100644
Binary files a/frontend/public/replay/red-bull/step2.jpg and b/frontend/public/replay/red-bull/step2.jpg differ
diff --git a/frontend/public/replay/red-bull/step3-heat.png b/frontend/public/replay/red-bull/step3-heat.png
index d263574..909662f 100644
Binary files a/frontend/public/replay/red-bull/step3-heat.png and b/frontend/public/replay/red-bull/step3-heat.png differ
diff --git a/frontend/public/replay/red-bull/step3.jpg b/frontend/public/replay/red-bull/step3.jpg
index 04c877f..7070b86 100644
Binary files a/frontend/public/replay/red-bull/step3.jpg and b/frontend/public/replay/red-bull/step3.jpg differ
diff --git a/frontend/public/replay/red-bull/step4-heat.png b/frontend/public/replay/red-bull/step4-heat.png
new file mode 100644
index 0000000..90398a5
Binary files /dev/null and b/frontend/public/replay/red-bull/step4-heat.png differ
diff --git a/frontend/public/replay/red-bull/step4.jpg b/frontend/public/replay/red-bull/step4.jpg
new file mode 100644
index 0000000..fdb6b21
Binary files /dev/null and b/frontend/public/replay/red-bull/step4.jpg differ
diff --git a/frontend/public/replay/red-bull/steps.json b/frontend/public/replay/red-bull/steps.json
index cdc8fdb..7274869 100644
--- a/frontend/public/replay/red-bull/steps.json
+++ b/frontend/public/replay/red-bull/steps.json
@@ -1,82 +1,102 @@
[
{
"step": 0,
- "directive": "make SEVERAL coordinated changes at once to turn this into a polished campaign: remove the clutter and any objects, hands, props or stray text crowding or blocking the Red Bull logo and product; reframe it head-on and enlarge it as the clear hero; clean and simplify the background; and add a bold, legible on-brand headline, call-to-action and wordmark on the Red Bull logo and product",
+ "directive": "remove the White device edge completely and fill its area with what would naturally be behind it. Keep the Red Bull ad, product, logo and slogan exactly as they are",
"variant_png": "replay/red-bull/step0.jpg",
"current_score": 0.79,
- "new_score": 0.6797,
- "delta": -0.1103,
+ "new_score": 0.79,
+ "delta": 0.0,
"target_salience_before": 0.4909,
- "target_salience_after": 0.2769,
- "judge": 0.9,
- "judge_reason": "This Red Bull ad creative is of high quality and excellent brand-fit. The image clearly showcases the product in a relatable, everyday setting (a desk with a computer), suggesting a context of work or study where a boost might be needed. The headline 'FUEL YOUR DRIVE' directly aligns with Red Bull's core promise of energy and motivation. The call to action 'GRAB YOUR WINGS' is a perfect, direct reference to Red Bull's iconic slogan, strongly reinforcing brand identity and recall. The typography is clean, legible, and well-placed. The overall composition is simple, effective, and professional. This ad would very likely run as a real ad due to its strong brand messaging, clear product focus, and targeting of a common consumption occasion for Red Bull.",
+ "target_salience_after": 0.4909,
+ "judge": 0.65,
+ "judge_reason": "The creative effectively places Red Bull in a relevant context for a segment of its target demographic: creatives and professionals working long hours with technology (iMac, drawing tablet). The product is clearly visible and centered. However, the image lacks the high-energy, dynamic, or visually striking aesthetic typically associated with Red Bull's main brand campaigns. It feels more like a well-executed stock photo or social media post than a high-impact advertising creative. While functional and demonstrating a common use case, it doesn't fully leverage the brand's 'gives you wings' essence in a visually compelling way, making it less likely to be a flagship ad but potentially suitable for digital content or targeted social media campaigns.",
"vetoed": false,
"guard": "reject",
"guard_reasons": [
- "attention score did not improve"
+ "change is imperceptible (ssim 1.0/0.9945) \u2014 likely reward-hack"
],
"improved": false,
- "n_directives": 8,
+ "n_directives": 4,
"variant_heatmap": "replay/red-bull/step0-heat.png"
},
{
"step": 1,
- "directive": "add a bold, legible, on-brand headline and call-to-action and strengthen the logo/wordmark right at the Red Bull logo and product as a strong, high-contrast focal point",
+ "directive": "remove the White smartphone back completely and fill its area with what would naturally be behind it. Keep the Red Bull ad, product, logo and slogan exactly as they are",
"variant_png": "replay/red-bull/step1.jpg",
"current_score": 0.79,
- "new_score": 0.591,
- "delta": -0.199,
+ "new_score": 0.79,
+ "delta": 0.0,
"target_salience_before": 0.4909,
- "target_salience_after": 0.1885,
- "judge": 0.85,
- "judge_reason": "This Red Bull ad creative demonstrates good quality and strong brand-fit. The image clearly features a Red Bull can in a relatable setting (a desk with tech gadgets), appealing to a demographic of professionals, students, or creatives who might need an energy boost. The slogan 'UNLEASH YOUR DAY' aligns well with Red Bull's core message of energy, performance, and overcoming limits, resonating with their 'Gives You Wiiiings' ethos in a practical, everyday context. The 'BUY NOW' CTA is clear and functional for a digital ad. The overall composition is clean and focused. While the visual aesthetic is slightly more subdued than Red Bull's typical high-octane imagery, it effectively targets a specific use-case and audience, making it plausible for a real ad campaign, especially for digital channels or an 'everyday energy' focus.",
+ "target_salience_after": 0.4909,
+ "judge": 0.9,
+ "judge_reason": "The ad creative is of high quality and excellent brand-fit. The image is crisp, well-lit, and the Red Bull can is prominently featured in a natural, yet intentional, setting. The context of a desk with a computer and a graphic tablet immediately associates Red Bull with productivity, creative work, or intense focus, aligning perfectly with the brand's messaging of providing energy and 'giving you wings' for demanding tasks. This is a very common and effective type of ad for Red Bull, suggesting it would absolutely run as a real ad, especially on social media or in lifestyle publications targeting students, professionals, or creatives.",
"vetoed": false,
"guard": "reject",
"guard_reasons": [
- "attention score did not improve"
+ "change is imperceptible (ssim 1.0/0.9945) \u2014 likely reward-hack"
],
"improved": false,
- "n_directives": 8,
+ "n_directives": 5,
"variant_heatmap": "replay/red-bull/step1-heat.png"
},
{
"step": 2,
- "directive": "clearly enlarge, brighten and sharpen the Red Bull logo and product so it becomes the single biggest, boldest focal element while gently dimming and de-cluttering the surroundings \u2014 a polished, real ad",
+ "directive": "remove the Back of phone completely and fill its area with what would naturally be behind it. Keep the Red Bull ad, product, logo and slogan exactly as they are",
"variant_png": "replay/red-bull/step2.jpg",
"current_score": 0.79,
- "new_score": 0.7721,
- "delta": -0.0179,
+ "new_score": 0.79,
+ "delta": 0.0,
"target_salience_before": 0.4909,
- "target_salience_after": 0.442,
+ "target_salience_after": 0.4909,
"judge": 0.9,
- "judge_reason": "This Red Bull ad creative is excellent in both quality and brand-fit. The image itself is high-quality, with good lighting, sharp focus on the product, and a clean, appealing composition. The context of a desk workspace (with a computer, drawing tablet, phone, and keyboard) is highly relatable to a significant portion of Red Bull's target audience, particularly students, professionals, and creatives who need an energy boost for focus and productivity. The iconic tagline 'GIVES YOU WINGS.' is perfectly placed, reinforcing the brand's core message directly and effectively. This ad perfectly embodies a common use-case for Red Bull in everyday life, making it authentic and impactful. It would absolutely run as a real ad across various digital and print platforms.",
+ "judge_reason": "This Red Bull ad creative demonstrates high quality and strong brand-fit. The image itself is professionally shot, with good lighting, composition, and focus on the product. The Red Bull can is prominently displayed in a natural, aspirational setting: a modern workspace with Apple products and a graphics tablet. This context effectively targets a segment of Red Bull's audience (creatives, professionals, students) who need energy and focus for 'body and mind' during work or study, aligning perfectly with the brand's 'gives you wings' slogan beyond just extreme sports. This type of lifestyle shot is very common in modern digital and social media advertising for energy drinks, making it highly plausible as a real ad creative.",
"vetoed": false,
"guard": "reject",
"guard_reasons": [
- "attention score did not improve"
+ "change is imperceptible (ssim 1.0/0.9945) \u2014 likely reward-hack"
],
"improved": false,
- "n_directives": 8,
+ "n_directives": 6,
"variant_heatmap": "replay/red-bull/step2-heat.png"
},
{
"step": 3,
- "directive": "remove or clean away any objects, hands, props or background obstructions that crowd or block the Red Bull logo and product so the product is fully visible, unobstructed, and the clear hero of the shot",
+ "directive": "remove the white device back completely and fill its area with what would naturally be behind it. Keep the Red Bull ad, product, logo and slogan exactly as they are",
"variant_png": "replay/red-bull/step3.jpg",
"current_score": 0.79,
- "new_score": 0.7172,
- "delta": -0.0728,
+ "new_score": 0.8039,
+ "delta": 0.0139,
"target_salience_before": 0.4909,
- "target_salience_after": 0.3309,
- "judge": 0.85,
- "judge_reason": "This creative has excellent brand-fit. It prominently features the Red Bull can and the iconic 'GIVES YOU WINGS' slogan, which immediately communicates the brand. The setting (a desk with a computer) is highly relevant, depicting a common scenario where Red Bull is consumed for energy or focus (e.g., studying, working, gaming). The image quality is good, with natural lighting and a clean aesthetic. While perhaps not as high-budget or action-packed as some Red Bull campaigns, this type of relatable, 'everyday life' integration is common for brands, especially in digital and social media advertising. It absolutely could run as a real ad.",
+ "target_salience_after": 0.5349,
+ "judge": 0.9,
+ "judge_reason": "This creative is high quality and demonstrates excellent brand-fit for Red Bull, and would very likely run as a real ad. The image quality is professional, with good lighting, composition, and focus on the product. The Red Bull can is prominently featured in a context that resonates strongly with a key segment of its target audience: creative professionals, students, or anyone engaged in desk-bound work requiring sustained focus. The presence of an iMac, a graphics tablet, and a smartphone subtly communicates a message of productivity, creativity, and the need for an energy boost to 'give you wiiings' during demanding tasks. This aligns perfectly with Red Bull's brand promise, extending beyond extreme sports to everyday mental performance.",
"vetoed": false,
- "guard": "reject",
+ "guard": "accept",
"guard_reasons": [
- "attention score did not improve"
+ "perceptible, localized/justified, and target salience improved"
],
- "improved": false,
- "n_directives": 8,
+ "improved": true,
+ "n_directives": 7,
"variant_heatmap": "replay/red-bull/step3-heat.png"
+ },
+ {
+ "step": 4,
+ "directive": "remove the Smartphone bottom edge completely and fill its area with what would naturally be behind it. Keep the Red Bull ad, product, logo and slogan exactly as they are",
+ "variant_png": "replay/red-bull/step4.jpg",
+ "current_score": 0.8039,
+ "new_score": 0.8095,
+ "delta": 0.0056,
+ "target_salience_before": 0.5349,
+ "target_salience_after": 0.5546,
+ "judge": 0.95,
+ "judge_reason": "This creative is high quality and has excellent brand-fit. The image clearly features the Red Bull can in a clean, well-lit setting that suggests focused, creative work (Mac computer, graphic tablet, keyboard). This context aligns perfectly with Red Bull's messaging around providing energy for both 'body and mind,' appealing to students, creatives, and professionals who need sustained focus. The photography is professional, with good composition and depth of field, making the product the clear focal point while the background adds relevant lifestyle context without being distracting. It would absolutely run as a real ad, particularly for digital channels or print ads targeting specific demographics like designers, developers, or students.",
+ "vetoed": false,
+ "guard": "accept",
+ "guard_reasons": [
+ "perceptible, localized/justified, and target salience improved"
+ ],
+ "improved": true,
+ "n_directives": 8,
+ "variant_heatmap": "replay/red-bull/step4-heat.png"
}
]
\ No newline at end of file
diff --git a/frontend/public/replay/spotify/step0-heat.png b/frontend/public/replay/spotify/step0-heat.png
index 5954042..cc3a2fd 100644
Binary files a/frontend/public/replay/spotify/step0-heat.png and b/frontend/public/replay/spotify/step0-heat.png differ
diff --git a/frontend/public/replay/spotify/step0.jpg b/frontend/public/replay/spotify/step0.jpg
index c773b19..c42d1ff 100644
Binary files a/frontend/public/replay/spotify/step0.jpg and b/frontend/public/replay/spotify/step0.jpg differ
diff --git a/frontend/public/replay/spotify/step1-heat.png b/frontend/public/replay/spotify/step1-heat.png
index 4108f56..cc3a2fd 100644
Binary files a/frontend/public/replay/spotify/step1-heat.png and b/frontend/public/replay/spotify/step1-heat.png differ
diff --git a/frontend/public/replay/spotify/step1.jpg b/frontend/public/replay/spotify/step1.jpg
index fbcef50..c42d1ff 100644
Binary files a/frontend/public/replay/spotify/step1.jpg and b/frontend/public/replay/spotify/step1.jpg differ
diff --git a/frontend/public/replay/spotify/step2-heat.png b/frontend/public/replay/spotify/step2-heat.png
index 753f7f8..cc3a2fd 100644
Binary files a/frontend/public/replay/spotify/step2-heat.png and b/frontend/public/replay/spotify/step2-heat.png differ
diff --git a/frontend/public/replay/spotify/step2.jpg b/frontend/public/replay/spotify/step2.jpg
index 0d16928..c42d1ff 100644
Binary files a/frontend/public/replay/spotify/step2.jpg and b/frontend/public/replay/spotify/step2.jpg differ
diff --git a/frontend/public/replay/spotify/step3-heat.png b/frontend/public/replay/spotify/step3-heat.png
index 4ff259c..cc3a2fd 100644
Binary files a/frontend/public/replay/spotify/step3-heat.png and b/frontend/public/replay/spotify/step3-heat.png differ
diff --git a/frontend/public/replay/spotify/step3.jpg b/frontend/public/replay/spotify/step3.jpg
index 2ebfbf2..c42d1ff 100644
Binary files a/frontend/public/replay/spotify/step3.jpg and b/frontend/public/replay/spotify/step3.jpg differ
diff --git a/frontend/public/replay/spotify/step4-heat.png b/frontend/public/replay/spotify/step4-heat.png
new file mode 100644
index 0000000..cc3a2fd
Binary files /dev/null and b/frontend/public/replay/spotify/step4-heat.png differ
diff --git a/frontend/public/replay/spotify/step4.jpg b/frontend/public/replay/spotify/step4.jpg
new file mode 100644
index 0000000..c42d1ff
Binary files /dev/null and b/frontend/public/replay/spotify/step4.jpg differ
diff --git a/frontend/public/replay/spotify/steps.json b/frontend/public/replay/spotify/steps.json
index e9aedf8..5174fd3 100644
--- a/frontend/public/replay/spotify/steps.json
+++ b/frontend/public/replay/spotify/steps.json
@@ -1,82 +1,102 @@
[
{
"step": 0,
- "directive": "make SEVERAL coordinated changes at once to turn this into a polished campaign: remove the clutter and any objects, hands, props or stray text crowding or blocking the Spotify logo and product; reframe it head-on and enlarge it as the clear hero; clean and simplify the background; and add a bold, legible on-brand headline, call-to-action and wordmark on the Spotify logo and product",
+ "directive": "remove the human hand completely and fill its area with what would naturally be behind it. Keep the Spotify ad, product, logo and slogan exactly as they are",
"variant_png": "replay/spotify/step0.jpg",
"current_score": 0.7596,
- "new_score": 0.4674,
- "delta": -0.2922,
+ "new_score": 0.7812,
+ "delta": 0.0216,
"target_salience_before": 0.6324,
- "target_salience_after": 0.1756,
- "judge": 0.9,
- "judge_reason": "This Spotify ad creative is of high quality and excellent brand-fit. The image is clean, well-composed, and features the product (Spotify app on a phone) prominently. The text 'MUSIC FOR EVERY MOMENT' and 'LISTEN FREE ON SPOTIFY' are clear, legible, and perfectly align with Spotify's core messaging and value proposition. The use of the official Spotify logo and wordmark ensures instant brand recognition. This ad effectively communicates key benefits (music for all occasions, free access) in a visually appealing and brand-consistent manner. It looks like a real ad Spotify would run.",
+ "target_salience_after": 0.7147,
+ "judge": 0.75,
+ "judge_reason": "The creative clearly features the Spotify logo on a mobile device, instantly communicating the brand and its primary mode of consumption. The image quality is good, and the casual, outdoor setting aligns well with Spotify's brand message of 'music on-the-go' and everyday usage. While it shows the loading screen rather than active music playback and the background is somewhat generic, it is a highly recognizable and suitable image for general brand awareness or app download campaigns, making it likely to run as a real ad.",
"vetoed": false,
- "guard": "reject",
+ "guard": "accept",
"guard_reasons": [
- "attention score did not improve"
+ "perceptible, localized/justified, and target salience improved"
],
- "improved": false,
- "n_directives": 7,
+ "improved": true,
+ "n_directives": 2,
"variant_heatmap": "replay/spotify/step0-heat.png"
},
{
"step": 1,
- "directive": "add a bold, legible, on-brand headline and call-to-action and strengthen the logo/wordmark right at the Spotify logo and product as a strong, high-contrast focal point",
+ "directive": "remove the Blurred background completely and fill its area with what would naturally be behind it. Keep the Spotify ad, product, logo and slogan exactly as they are",
"variant_png": "replay/spotify/step1.jpg",
- "current_score": 0.7596,
- "new_score": 0.7798,
- "delta": 0.0202,
- "target_salience_before": 0.6324,
- "target_salience_after": 0.7088,
- "judge": 0.9,
- "judge_reason": "This Spotify ad creative demonstrates high quality and strong brand-fit, making it very likely to run as a real ad. The image is clean, well-lit, and professionally composed, focusing clearly on the smartphone with the Spotify app displayed. The use of the prominent Spotify logo, a relevant tagline ('STREAM YOUR WORLD'), and a clear call to action ('DOWNLOAD NOW') aligns perfectly with typical app marketing strategies. The casual, everyday setting (person holding a phone, blurred outdoor background) resonates with how people naturally interact with streaming services, emphasizing convenience and personal entertainment. The aesthetic is modern and fits Spotify's established brand image. The only minor critique is the visible smudges on the phone screen in some crops, which is a small detail but could be polished for absolute perfection.",
+ "current_score": 0.7812,
+ "new_score": 0.7812,
+ "delta": 0.0,
+ "target_salience_before": 0.7147,
+ "target_salience_after": 0.7147,
+ "judge": 0.85,
+ "judge_reason": "This creative has high quality and strong brand-fit. The image is clear, well-composed, and features the prominent Spotify logo on a modern smartphone, instantly conveying the brand. The natural lighting and casual 'in-hand' setting contribute to an authentic, lifestyle feel, which aligns well with how Spotify often positions itself as an integrated part of daily life. The background blur effectively draws attention to the product. It would absolutely run as a real ad, likely accompanied by a call-to-action or tagline, as it effectively shows the brand in a relatable usage context. The minor glare/smudge on the screen is a small imperfection but also adds to the 'real-world' authenticity rather than detracting significantly.",
"vetoed": false,
- "guard": "accept",
+ "guard": "reject",
"guard_reasons": [
- "perceptible, localized/justified, and target salience improved"
+ "change is imperceptible (ssim 1.0/1.0) \u2014 likely reward-hack"
],
- "improved": true,
- "n_directives": 7,
+ "improved": false,
+ "n_directives": 5,
"variant_heatmap": "replay/spotify/step1-heat.png"
},
{
"step": 2,
- "directive": "clearly enlarge, brighten and sharpen the Spotify logo and product so it becomes the single biggest, boldest focal element while gently dimming and de-cluttering the surroundings \u2014 a polished, real ad",
+ "directive": "remove the Blurred light surface completely and fill its area with what would naturally be behind it. Keep the Spotify ad, product, logo and slogan exactly as they are",
"variant_png": "replay/spotify/step2.jpg",
- "current_score": 0.7798,
- "new_score": 0.7747,
- "delta": -0.0051,
- "target_salience_before": 0.7088,
- "target_salience_after": 0.6884,
- "judge": 0.9,
- "judge_reason": "This Spotify ad creative is of high quality and excellent brand-fit. The image is clear, well-composed, and focuses directly on the product \u2013 the Spotify app on a smartphone. The iconic Spotify logo is prominently displayed, instantly recognizable. The tagline 'STREAM YOUR WORLD' effectively communicates Spotify's value proposition of extensive content and personalization. The clear call to action 'DOWNLOAD NOW' is direct and actionable. The overall aesthetic is clean, modern, and perfectly aligns with Spotify's brand identity. This creative would absolutely run as a real ad, as it is effective, on-brand, and communicates its message clearly.",
+ "current_score": 0.7812,
+ "new_score": 0.7812,
+ "delta": 0.0,
+ "target_salience_before": 0.7147,
+ "target_salience_after": 0.7147,
+ "judge": 0.65,
+ "judge_reason": "The creative clearly features the Spotify logo on a mobile phone, placing it in a casual, real-world context which aligns with Spotify's brand image of accessibility and everyday use. The photo quality is good, with clear focus and natural lighting. However, the ad creative's effectiveness is limited because it shows the app's loading screen rather than demonstrating active use or highlighting any specific features or benefits of Spotify (e.g., listening to music, podcasts, personalized playlists). This makes it a very generic brand awareness piece that lacks dynamism and a compelling call to action or value proposition. The visible smudges on the phone screen are also a minor detractor for a polished ad. While it *could* run as a simple brand reinforcement ad, it's not a particularly strong or innovative creative that would stand out or drive significant engagement compared to ads showcasing actual listening experiences.",
"vetoed": false,
"guard": "reject",
"guard_reasons": [
- "attention score did not improve"
+ "change is imperceptible (ssim 1.0/1.0) \u2014 likely reward-hack"
],
"improved": false,
- "n_directives": 7,
+ "n_directives": 6,
"variant_heatmap": "replay/spotify/step2-heat.png"
},
{
"step": 3,
- "directive": "remove or clean away any objects, hands, props or background obstructions that crowd or block the Spotify logo and product so the product is fully visible, unobstructed, and the clear hero of the shot",
+ "directive": "remove the Blue fabric texture completely and fill its area with what would naturally be behind it. Keep the Spotify ad, product, logo and slogan exactly as they are",
"variant_png": "replay/spotify/step3.jpg",
- "current_score": 0.7798,
- "new_score": 0.7054,
- "delta": -0.0744,
- "target_salience_before": 0.7088,
- "target_salience_after": 0.4793,
+ "current_score": 0.7812,
+ "new_score": 0.7812,
+ "delta": 0.0,
+ "target_salience_before": 0.7147,
+ "target_salience_after": 0.7147,
"judge": 0.9,
- "judge_reason": "This creative is high quality and an excellent brand fit for Spotify. The first-person perspective, looking down at a phone with the Spotify interface, creates a relatable and authentic feel, aligning with Spotify's casual and accessible brand image. The prominent display of the Spotify logo and the 'SPOTIFY PREMIUM' call to action are clear and well-branded. The text 'STREAM YOUR WORLD' and 'DOWNLOAD NOW' clearly communicate value and provide a direct call to action. The overall aesthetic is clean, modern, and perfectly suited for a mobile-first advertising environment. This creative would absolutely run as a real ad, likely across social media platforms or in app stores, effectively targeting potential users or those considering Premium.",
+ "judge_reason": "This creative is high quality and a strong brand-fit for Spotify. The image clearly displays the Spotify logo on a modern smartphone, held in a natural, casual setting. The focus is sharp on the phone and logo, with a pleasingly blurred background that suggests an everyday, outdoor environment. This aligns perfectly with Spotify's brand messaging of music being accessible and part of daily life. It looks authentic, not overly staged, which is a common and effective style for app advertising. It would absolutely run as a real ad, perhaps for general brand awareness or to promote 'music for any moment.' The only very minor critiques could be some visible smudges/fingerprints on the screen, which is realistic but some brands prefer perfectly pristine screens in ads, and the visible time/signal which are neutral elements.",
"vetoed": false,
"guard": "reject",
"guard_reasons": [
- "attention score did not improve"
+ "change is imperceptible (ssim 1.0/1.0) \u2014 likely reward-hack"
],
"improved": false,
- "n_directives": 7,
+ "n_directives": 6,
"variant_heatmap": "replay/spotify/step3-heat.png"
+ },
+ {
+ "step": 4,
+ "directive": "remove the Hand holding object completely and fill its area with what would naturally be behind it. Keep the Spotify ad, product, logo and slogan exactly as they are",
+ "variant_png": "replay/spotify/step4.jpg",
+ "current_score": 0.7812,
+ "new_score": 0.7812,
+ "delta": 0.0,
+ "target_salience_before": 0.7147,
+ "target_salience_after": 0.7147,
+ "judge": 0.9,
+ "judge_reason": "This creative has excellent brand-fit and good quality. It clearly features the Spotify logo on a smartphone screen, which is instantly recognizable. The casual, outdoor setting (implied by the blurred background and casual attire) aligns perfectly with Spotify's brand image of music for every moment and location. The image quality is clear, well-lit, and focused on the product. It's a simple, effective, and relatable visual that could easily run as a real ad, especially for brand awareness or promoting mobile app usage.",
+ "vetoed": false,
+ "guard": "reject",
+ "guard_reasons": [
+ "change is imperceptible (ssim 1.0/1.0) \u2014 likely reward-hack"
+ ],
+ "improved": false,
+ "n_directives": 6,
+ "variant_heatmap": "replay/spotify/step4-heat.png"
}
]
\ No newline at end of file
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 7f45040..667668e 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -135,7 +135,8 @@ export default function App() {
setBusy(`Retoucher · branch ${ctl.step + 1} with Nano Banana…`);
try {
const src = ctl.step === 0 ? file : await dataUrlToFile(ctl.bestImgUrl, "best.png");
- const res = await optimizeStep(src, brand, ctl.step, active?.target_box, hint, active?.id);
+ const tried = (agents?.tree ?? []).filter((n) => n.parent !== null).map((n) => n.directive);
+ const res = await optimizeStep(src, brand, ctl.step, active?.target_box, hint, active?.id, tried);
const newScore = res.new_score; // real size-invariant prominence (can be lower)
const improved = res.improved; // real: rose AND not vetoed AND guard accepted
const becomesBest = improved && newScore > ctl.bestScore;
diff --git a/frontend/src/api.ts b/frontend/src/api.ts
index abd5e3f..63049f6 100644
--- a/frontend/src/api.ts
+++ b/frontend/src/api.ts
@@ -119,6 +119,7 @@ export async function health(): Promise {
export async function optimizeStep(
image: File, brand: string, step: number, target?: Box, hint?: string, sample?: string,
+ tried: string[] = [],
): Promise {
if (DEMO) {
if (!sample) throw new Error("The demo replays the sample ads only");
@@ -130,6 +131,7 @@ export async function optimizeStep(
fd.append("step", String(step));
if (target) fd.append("target", JSON.stringify(target));
if (hint && hint.trim()) fd.append("hint", hint.trim()); // optional user suggestion to Nano Banana
+ fd.append("tried", JSON.stringify(tried)); // so the backend picks the next untried branch
const r = await fetch("/optimize/step", { method: "POST", body: fd });
if (!r.ok) throw new Error(`/optimize/step ${r.status}`);
return r.json();
diff --git a/results/red-bull-presets/step0.jpg b/results/red-bull-presets/step0.jpg
new file mode 100644
index 0000000..5de9fd5
Binary files /dev/null and b/results/red-bull-presets/step0.jpg differ
diff --git a/results/red-bull-presets/step1.jpg b/results/red-bull-presets/step1.jpg
new file mode 100644
index 0000000..117a80c
Binary files /dev/null and b/results/red-bull-presets/step1.jpg differ
diff --git a/results/red-bull-presets/step2.jpg b/results/red-bull-presets/step2.jpg
new file mode 100644
index 0000000..a4b0681
Binary files /dev/null and b/results/red-bull-presets/step2.jpg differ
diff --git a/results/red-bull-presets/step3.jpg b/results/red-bull-presets/step3.jpg
new file mode 100644
index 0000000..04c877f
Binary files /dev/null and b/results/red-bull-presets/step3.jpg differ
diff --git a/results/red-bull-presets/steps.json b/results/red-bull-presets/steps.json
new file mode 100644
index 0000000..cdc8fdb
--- /dev/null
+++ b/results/red-bull-presets/steps.json
@@ -0,0 +1,82 @@
+[
+ {
+ "step": 0,
+ "directive": "make SEVERAL coordinated changes at once to turn this into a polished campaign: remove the clutter and any objects, hands, props or stray text crowding or blocking the Red Bull logo and product; reframe it head-on and enlarge it as the clear hero; clean and simplify the background; and add a bold, legible on-brand headline, call-to-action and wordmark on the Red Bull logo and product",
+ "variant_png": "replay/red-bull/step0.jpg",
+ "current_score": 0.79,
+ "new_score": 0.6797,
+ "delta": -0.1103,
+ "target_salience_before": 0.4909,
+ "target_salience_after": 0.2769,
+ "judge": 0.9,
+ "judge_reason": "This Red Bull ad creative is of high quality and excellent brand-fit. The image clearly showcases the product in a relatable, everyday setting (a desk with a computer), suggesting a context of work or study where a boost might be needed. The headline 'FUEL YOUR DRIVE' directly aligns with Red Bull's core promise of energy and motivation. The call to action 'GRAB YOUR WINGS' is a perfect, direct reference to Red Bull's iconic slogan, strongly reinforcing brand identity and recall. The typography is clean, legible, and well-placed. The overall composition is simple, effective, and professional. This ad would very likely run as a real ad due to its strong brand messaging, clear product focus, and targeting of a common consumption occasion for Red Bull.",
+ "vetoed": false,
+ "guard": "reject",
+ "guard_reasons": [
+ "attention score did not improve"
+ ],
+ "improved": false,
+ "n_directives": 8,
+ "variant_heatmap": "replay/red-bull/step0-heat.png"
+ },
+ {
+ "step": 1,
+ "directive": "add a bold, legible, on-brand headline and call-to-action and strengthen the logo/wordmark right at the Red Bull logo and product as a strong, high-contrast focal point",
+ "variant_png": "replay/red-bull/step1.jpg",
+ "current_score": 0.79,
+ "new_score": 0.591,
+ "delta": -0.199,
+ "target_salience_before": 0.4909,
+ "target_salience_after": 0.1885,
+ "judge": 0.85,
+ "judge_reason": "This Red Bull ad creative demonstrates good quality and strong brand-fit. The image clearly features a Red Bull can in a relatable setting (a desk with tech gadgets), appealing to a demographic of professionals, students, or creatives who might need an energy boost. The slogan 'UNLEASH YOUR DAY' aligns well with Red Bull's core message of energy, performance, and overcoming limits, resonating with their 'Gives You Wiiiings' ethos in a practical, everyday context. The 'BUY NOW' CTA is clear and functional for a digital ad. The overall composition is clean and focused. While the visual aesthetic is slightly more subdued than Red Bull's typical high-octane imagery, it effectively targets a specific use-case and audience, making it plausible for a real ad campaign, especially for digital channels or an 'everyday energy' focus.",
+ "vetoed": false,
+ "guard": "reject",
+ "guard_reasons": [
+ "attention score did not improve"
+ ],
+ "improved": false,
+ "n_directives": 8,
+ "variant_heatmap": "replay/red-bull/step1-heat.png"
+ },
+ {
+ "step": 2,
+ "directive": "clearly enlarge, brighten and sharpen the Red Bull logo and product so it becomes the single biggest, boldest focal element while gently dimming and de-cluttering the surroundings \u2014 a polished, real ad",
+ "variant_png": "replay/red-bull/step2.jpg",
+ "current_score": 0.79,
+ "new_score": 0.7721,
+ "delta": -0.0179,
+ "target_salience_before": 0.4909,
+ "target_salience_after": 0.442,
+ "judge": 0.9,
+ "judge_reason": "This Red Bull ad creative is excellent in both quality and brand-fit. The image itself is high-quality, with good lighting, sharp focus on the product, and a clean, appealing composition. The context of a desk workspace (with a computer, drawing tablet, phone, and keyboard) is highly relatable to a significant portion of Red Bull's target audience, particularly students, professionals, and creatives who need an energy boost for focus and productivity. The iconic tagline 'GIVES YOU WINGS.' is perfectly placed, reinforcing the brand's core message directly and effectively. This ad perfectly embodies a common use-case for Red Bull in everyday life, making it authentic and impactful. It would absolutely run as a real ad across various digital and print platforms.",
+ "vetoed": false,
+ "guard": "reject",
+ "guard_reasons": [
+ "attention score did not improve"
+ ],
+ "improved": false,
+ "n_directives": 8,
+ "variant_heatmap": "replay/red-bull/step2-heat.png"
+ },
+ {
+ "step": 3,
+ "directive": "remove or clean away any objects, hands, props or background obstructions that crowd or block the Red Bull logo and product so the product is fully visible, unobstructed, and the clear hero of the shot",
+ "variant_png": "replay/red-bull/step3.jpg",
+ "current_score": 0.79,
+ "new_score": 0.7172,
+ "delta": -0.0728,
+ "target_salience_before": 0.4909,
+ "target_salience_after": 0.3309,
+ "judge": 0.85,
+ "judge_reason": "This creative has excellent brand-fit. It prominently features the Red Bull can and the iconic 'GIVES YOU WINGS' slogan, which immediately communicates the brand. The setting (a desk with a computer) is highly relevant, depicting a common scenario where Red Bull is consumed for energy or focus (e.g., studying, working, gaming). The image quality is good, with natural lighting and a clean aesthetic. While perhaps not as high-budget or action-packed as some Red Bull campaigns, this type of relatable, 'everyday life' integration is common for brands, especially in digital and social media advertising. It absolutely could run as a real ad.",
+ "vetoed": false,
+ "guard": "reject",
+ "guard_reasons": [
+ "attention score did not improve"
+ ],
+ "improved": false,
+ "n_directives": 8,
+ "variant_heatmap": "replay/red-bull/step3-heat.png"
+ }
+]
\ No newline at end of file
diff --git a/scripts/make_showcase.py b/scripts/make_showcase.py
index 3ca103b..24e6c9f 100644
--- a/scripts/make_showcase.py
+++ b/scripts/make_showcase.py
@@ -180,13 +180,14 @@ def add(im, ms):
def fig_every_edit(sample: str = "red-bull"):
- """assets/media/every-edit.png - every Nano Banana edit from one recorded optimizer run
- (frontend/public/replay, made by scripts/record_demo.py), with the scores DeepGaze gave
- each one against the same fixed brand box."""
+ """assets/media/every-edit.png - every Nano Banana edit from one recorded run of the old
+ preset branches (headline, CTA, several changes at once), saved in
+ results/red-bull-presets, with the scores DeepGaze gave each one against the same
+ fixed brand box."""
import json
- run_dir = ROOT / "frontend" / "public" / "replay" / sample
+ run_dir = ROOT / "results" / (sample + "-presets")
if not (run_dir / "steps.json").exists():
- print("every-edit.png skipped: run scripts/record_demo.py first")
+ print("every-edit.png skipped: no saved preset run")
return
steps = json.loads((run_dir / "steps.json").read_text(encoding="utf-8"))
box = next(s["box"] for s in load_samples() if s["id"] == sample)
diff --git a/scripts/record_demo.py b/scripts/record_demo.py
index 604bb13..c3e4dfd 100644
--- a/scripts/record_demo.py
+++ b/scripts/record_demo.py
@@ -3,6 +3,8 @@
python scripts/record_demo.py # every sample that isn't recorded yet
python scripts/record_demo.py nike apple # just these (re-records them)
python scripts/record_demo.py --rejudge # re-run the current guard on saved runs
+ python scripts/record_demo.py nike --steps=3 # fewer branches, to save image edits
+ python scripts/record_demo.py nike --resume=3 # keep 3 recorded steps, redo the rest
Calls the real FastAPI app in-process, the same way the frontend does: /predict on the
sample, then /optimize/step a few times, re-sending the current best creative after each
@@ -33,7 +35,7 @@
SAMPLES_TS = ROOT / "frontend" / "src" / "samples.ts"
PUBLIC = ROOT / "frontend" / "public"
OUT = PUBLIC / "replay"
-STEPS = 4
+STEPS = 5
EDIT_MAX = 1400 # longest side of the saved edit images; scoring happens at full size
@@ -56,7 +58,7 @@ def save_jpg(raw: bytes, path: Path, max_side: int) -> None:
img.save(path, quality=84, optimize=True, progressive=True)
-def record(client: TestClient, s: dict) -> None:
+def record(client: TestClient, s: dict, resume: int = 0) -> None:
sid, box = s["id"], json.dumps(s["box"])
out = OUT / sid
out.mkdir(parents=True, exist_ok=True)
@@ -79,10 +81,17 @@ def record(client: TestClient, s: dict) -> None:
pred["distractors"][0]["desc"] if pred["distractors"] else "-"))
best, best_score, steps = src, pred["attention_score"], []
- for k in range(STEPS):
+ if resume: # keep the first `resume` recorded steps and continue from their best image
+ steps = json.loads((out / "steps.json").read_text(encoding="utf-8"))[:resume]
+ for k, res in enumerate(steps):
+ if res["improved"] and res["new_score"] > best_score:
+ # the saved display copy; DeepGaze scores at 1024 px either way
+ best, best_score = (out / "step{}.jpg".format(k)).read_bytes(), res["new_score"]
+ for k in range(len(steps), STEPS):
t = time.time()
r = client.post("/optimize/step", files={"image": ("best.png", best, "image/png")},
- data={"brand": s["brand"], "target": box, "step": str(k)})
+ data={"brand": s["brand"], "target": box, "step": str(k),
+ "tried": json.dumps([x["directive"] for x in steps])})
r.raise_for_status()
res = r.json()
if res["directive"].startswith("["):
@@ -100,6 +109,8 @@ def record(client: TestClient, s: dict) -> None:
print("{:14s} step {} {:.0f} -> {:.0f} judge {:.2f} guard {:6s} improved={} ({:.0f}s)".format(
sid, k, res["current_score"] * 100, res["new_score"] * 100, res["judge"],
res["guard"], res["improved"], time.time() - t))
+ if k + 1 >= res["n_directives"]: # no untried branch left on this ad
+ break
(out / "steps.json").write_text(json.dumps(steps, indent=1), encoding="utf-8")
@@ -134,7 +145,11 @@ def main_() -> None:
if (OUT / s["id"] / "steps.json").exists():
rejudge(s)
return
- want = sys.argv[1:]
+ global STEPS
+ args = [a for a in sys.argv[1:] if not a.startswith(("--steps=", "--resume="))]
+ resume = next((int(a.split("=")[1]) for a in sys.argv[1:] if a.startswith("--resume=")), 0)
+ STEPS = next((int(a.split("=")[1]) for a in sys.argv[1:] if a.startswith("--steps=")), STEPS)
+ want = args
client = TestClient(main.app)
with client:
for s in samples():
@@ -142,7 +157,7 @@ def main_() -> None:
continue
if not want and (OUT / s["id"] / "steps.json").exists():
continue
- record(client, s)
+ record(client, s, resume)
rec = sorted(p.name for p in OUT.iterdir() if (p / "steps.json").exists())
(OUT / "manifest.json").write_text(json.dumps(
{"recorded": date.today().isoformat(), "engine": "deepgaze-iie", "device": dg.device_name(),