feat: resume fixes, OpenRouter templates, and project context
Delegate expected output filenames to modules so resume works for rembg and composite; normalize G'MIC multi-frame output; add OpenRouter style reference support with tests. Add Crusaders, orange, and team gallery pipelines plus SOUL/AGENTS context files. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Match new team member photos to an existing player gallery style via OpenRouter."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from imagepipeline import Pipeline
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
ENV_FILE = REPO_ROOT / ".env"
|
||||
|
||||
# Folder with new member photos to style-match.
|
||||
INPUT = Path("/home/frank/tmp/spieler")
|
||||
|
||||
# One existing gallery player image as style reference.
|
||||
TEMPLATE_IMAGE = Path("/home/frank/Downloads/2026_AHC_44_Julius-Nikolaus_Dittus-524-scaled.webp")
|
||||
|
||||
OUTPUT_BASE = Path.home() / "pipeline_output"
|
||||
|
||||
OPENROUTER_MODEL = "google/gemini-3-pro-image"
|
||||
|
||||
GALLERY_MATCH_PROMPT = (
|
||||
"Match the second image to the first image's gallery style so it fits seamlessly "
|
||||
"alongside the other players. Match color grading, white balance, contrast, "
|
||||
"saturation, lighting direction, background treatment, sharpness, and overall "
|
||||
"polish. "
|
||||
"CRITICAL: Do not alter the person's face, identity, facial features, expression, "
|
||||
"hair, pose, body shape, or clothing details. Do not crop, reframe, or change the "
|
||||
"aspect ratio. Do not add or remove people or objects. "
|
||||
"Keep the exact same image dimensions and composition — only adjust global style "
|
||||
"and color to match the reference."
|
||||
)
|
||||
|
||||
|
||||
def _load_env_file(path: Path) -> None:
|
||||
if not path.is_file():
|
||||
return
|
||||
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
key, sep, value = line.partition("=")
|
||||
if not sep:
|
||||
continue
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'":
|
||||
value = value[1:-1]
|
||||
os.environ.setdefault(key, value)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
_load_env_file(ENV_FILE)
|
||||
|
||||
with Pipeline(
|
||||
name="team_gallery_match",
|
||||
input_dir=INPUT,
|
||||
output_base=OUTPUT_BASE,
|
||||
) as p:
|
||||
p.step(
|
||||
"openrouter_edit",
|
||||
inputs="input",
|
||||
prompt=GALLERY_MATCH_PROMPT,
|
||||
model=OPENROUTER_MODEL,
|
||||
template_image=TEMPLATE_IMAGE,
|
||||
# Downscale for API, then upscale back to original dimensions.
|
||||
# Use 0 only if you accept higher cost/latency for ~10 MB sources.
|
||||
max_edge=4096,
|
||||
skip_existing=True,
|
||||
)
|
||||
output_root = p.run()
|
||||
|
||||
print(f"Pipeline finished. Output: {output_root}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user