from collections import deque from pathlib import Path from PIL import Image, ImageDraw, ImageEnhance, ImageFilter, ImageFont OUT = Path(r"C:\Users\tangl\Desktop\Git\agent-foundation\output\jump_rope_video") OUT.mkdir(parents=True, exist_ok=True) W, H = 1920, 1080 FONT_PATH = r"C:\Windows\Fonts\msyh.ttc" FONT_BOLD_PATH = r"C:\Windows\Fonts\msyhbd.ttc" def font(size: int, bold: bool = False): return ImageFont.truetype(FONT_BOLD_PATH if bold else FONT_PATH, size) def cover(img: Image.Image, size=(W, H)) -> Image.Image: img = img.convert("RGB") ratio = max(size[0] / img.width, size[1] / img.height) resized = img.resize((round(img.width * ratio), round(img.height * ratio)), Image.Resampling.LANCZOS) x = (resized.width - size[0]) // 2 y = (resized.height - size[1]) // 2 return resized.crop((x, y, x + size[0], y + size[1])) def fit(img: Image.Image, size, bg=(0, 0, 0)) -> Image.Image: img = img.convert("RGBA") ratio = min(size[0] / img.width, size[1] / img.height) resized = img.resize((round(img.width * ratio), round(img.height * ratio)), Image.Resampling.LANCZOS) canvas = Image.new("RGBA", size, bg + (255,)) canvas.alpha_composite(resized, ((size[0] - resized.width) // 2, (size[1] - resized.height) // 2)) return canvas def vignette(img: Image.Image, strength=0.42) -> Image.Image: img = img.convert("RGB") mask = Image.new("L", (W, H), 255) px = mask.load() cx, cy = W / 2, H / 2 maxd = (cx * cx + cy * cy) ** 0.5 for y in range(H): for x in range(W): d = (((x - cx) ** 2 + (y - cy) ** 2) ** 0.5) / maxd px[x, y] = int(255 * max(1 - strength * d * d, 0.45)) black = Image.new("RGB", (W, H), "black") return Image.composite(img, black, mask) def add_caption(img: Image.Image, headline: str, subline: str, align="left") -> Image.Image: img = img.convert("RGBA") overlay = Image.new("RGBA", (W, H), (0, 0, 0, 0)) d = ImageDraw.Draw(overlay) if align == "left": x, anchor = 118, "la" else: x, anchor = W - 118, "ra" y = 735 d.rounded_rectangle((72, 690, W - 72, 970), radius=28, fill=(0, 13, 30, 92)) d.text((x, y), headline, font=font(64, True), fill=(232, 241, 62, 255), anchor=anchor) d.text((x, y + 88), subline, font=font(36), fill=(242, 247, 250, 245), anchor=anchor) d.line((x if align == "left" else x - 480, y - 30, x + 480 if align == "left" else x, y - 30), fill=(232, 241, 62, 220), width=4) return Image.alpha_composite(img, overlay).convert("RGB") def remove_connected_background(img: Image.Image) -> Image.Image: rgba = img.convert("RGBA") rgb = rgba.convert("RGB") pix = rgb.load() alpha = Image.new("L", rgb.size, 255) apix = alpha.load() seen = bytearray(rgb.width * rgb.height) q = deque() def bg_like(x, y): r, g, b = pix[x, y] return min(r, g, b) > 150 and max(r, g, b) - min(r, g, b) < 34 for x in range(rgb.width): q.append((x, 0)); q.append((x, rgb.height - 1)) for y in range(rgb.height): q.append((0, y)); q.append((rgb.width - 1, y)) while q: x, y = q.popleft() idx = y * rgb.width + x if seen[idx]: continue seen[idx] = 1 if not bg_like(x, y): continue apix[x, y] = 0 if x: q.append((x - 1, y)) if x + 1 < rgb.width: q.append((x + 1, y)) if y: q.append((x, y - 1)) if y + 1 < rgb.height: q.append((x, y + 1)) alpha = alpha.filter(ImageFilter.GaussianBlur(1.2)) rgba.putalpha(alpha) bbox = alpha.getbbox() return rgba.crop(bbox) if bbox else rgba landing = Image.open(r"C:\Users\tangl\.codex\generated_images\019f88f8-7dd3-7c53-8d9a-b3544a768a7e\exec-872cf44f-8972-429c-92a9-9a1946daa114.png") bend = Image.open(r"C:\Users\tangl\.codex\generated_images\019f88f8-7dd3-7c53-8d9a-b3544a768a7e\exec-38051cbd-14da-49fc-a139-d5699968df0a.png") side = Image.open(r"C:\Users\tangl\xwechat_files\wxid_tjrokx4gscnl21_4d57\temp\RWTemp\2026-07\afdb9db85f27cdd1cc9425d3ab16ec83.jpg") front_l = Image.open(r"C:\Users\tangl\xwechat_files\wxid_tjrokx4gscnl21_4d57\temp\RWTemp\2026-07\a9fb3a735b799f5909ae993223228a9b.jpg") front_r = Image.open(r"C:\Users\tangl\xwechat_files\wxid_tjrokx4gscnl21_4d57\temp\RWTemp\2026-07\b0479771abcc3bc2b77bb4953e6d6b7e.jpg") sole = Image.open(r"C:\Users\tangl\xwechat_files\wxid_tjrokx4gscnl21_4d57\temp\RWTemp\2026-07\b2717ca770f0c7cfa0110af900a8a371.jpg") hold_ref = Image.open(r"C:\Users\tangl\AppData\Local\Temp\codex-clipboard-bde03ba2-b96f-4712-99a2-c9807418603d.png") end_ref = Image.open(r"C:\Users\tangl\AppData\Local\Temp\codex-clipboard-6858372d-d6d7-47b9-ac51-73f718e298eb.png") frame1 = add_caption(vignette(cover(landing)), "轻盈落地 快速回弹", "前掌触地,蓄力即发") frame1.save(OUT / "01_landing.jpg", quality=95) frame2 = add_caption(vignette(cover(bend)), "混合飞织 超强透气", "超薄前掌,轻盈如风", "right") frame2.save(OUT / "02_flex.jpg", quality=95) # Dual-shoe product composition that reads as a synchronized bounce close-up. bg = Image.new("RGB", (W, H), (10, 44, 69)) d = ImageDraw.Draw(bg) for y in range(H): t = y / H color = (int(16 - 8 * t), int(75 - 25 * t), int(106 - 34 * t)) d.line((0, y, W, y), fill=color) for x in (310, 790, 1270, 1750): d.line((x, 0, x - 420, H), fill=(89, 142, 166), width=6) left_shoe = fit(front_l, (820, 650), (232, 233, 229)).convert("RGB") right_shoe = fit(front_r, (820, 650), (232, 233, 229)).convert("RGB") bg.paste(left_shoe, (105, 150)) bg.paste(right_shoe, (995, 150)) frame3 = add_caption(vignette(bg, 0.32), "弹跳力 MAX", "Y型支撑结构|减震灵活") frame3.save(OUT / "03_bounce.jpg", quality=95) frame4 = add_caption(vignette(cover(side)), "为速度而生", "跳绳竞速鞋|轻、弹、稳", "right") frame4.save(OUT / "04_side.jpg", quality=95) frame5 = add_caption(vignette(cover(sole)), "全掌抓地", "紫色橡胶外底|快速启停") frame5.save(OUT / "05_outsole.jpg", quality=95) # Recreate the hand-held hero composition as a clean floating product panel over the supplied athlete backdrop. hold_bg = ImageEnhance.Brightness(cover(hold_ref)).enhance(0.72).filter(ImageFilter.GaussianBlur(4)) hero = hold_bg.convert("RGBA") panel_size = (1240, 620) panel = fit(side, panel_size, (238, 238, 235)) mask = Image.new("L", panel_size, 0) ImageDraw.Draw(mask).rounded_rectangle((0, 0, panel_size[0] - 1, panel_size[1] - 1), radius=34, fill=238) panel.putalpha(mask) shadow_mask = mask.filter(ImageFilter.GaussianBlur(24)) shadow = Image.new("RGBA", panel_size, (0, 0, 0, 0)) shadow.putalpha(shadow_mask.point(lambda a: int(a * 0.42))) pos = ((W - panel_size[0]) // 2, 205) hero.alpha_composite(shadow, (pos[0] + 16, pos[1] + 25)) hero.alpha_composite(panel, pos) draw = ImageDraw.Draw(hero) draw.text((W // 2, 880), "专业跳绳竞速鞋", font=font(52, True), fill="white", anchor="mm") draw.text((W // 2, 952), "LIGHT · REBOUND · STABILITY", font=font(25), fill=(225, 234, 238), anchor="mm") hero.convert("RGB").save(OUT / "06_hero.jpg", quality=95) frame7 = fit(end_ref, (W, H), (0, 0, 0)).convert("RGB") frame7.save(OUT / "07_end.jpg", quality=98) print(OUT)