15 KiB
Image Tools
Design boundary: keep provider credentials explicit, keep in-pipeline acquisition manifest-driven, and treat external image references as authoring inputs while delivery writes self-contained SVG previews and native PPTX media.
Image tools cover formula rendering, prompt-based AI generation, web image search, image inspection, and Gemini watermark removal.
latex_render.py
Manifest-driven LaTeX formula renderer. Default Generate has Strategist write
images/formula_manifest.json after Typography confirmation; Quick Generate
has the current agent write the same resource manifest without confirmation.
This script renders only those declared formulas to transparent PNGs and writes
dimensions back into the manifest.
python3 scripts/latex_render.py <project_path>
python3 scripts/latex_render.py <project_path> --dry-run
python3 scripts/latex_render.py <project_path> --providers codecogs,quicklatex,mathpad,wikimedia
Manifest shape:
{
"providers": ["codecogs", "quicklatex", "mathpad", "wikimedia"],
"items": [
{
"id": "formula_001",
"latex": "E = mc^2",
"display": "block",
"color": "#1D1D1F",
"background": "#FFFFFF",
"transparent": true,
"dpi": 300,
"filename": "formula_001.png"
}
]
}
Output files land directly under project/images/. Formula filenames should use a shared formula_ prefix, e.g. formula_001.png. The default provider chain is codecogs,quicklatex,mathpad,wikimedia; each provider is tried automatically until one succeeds, and the winning provider is recorded back into the manifest. --providers or manifest-level providers may override the order, but all four are available as no-key fallbacks. Formula PNGs are transparent by default. background is the temporary render matte and local background-removal reference; set transparent: false only when an opaque final formula asset is intentional. The script does not scan spec_lock.md or source documents for $...$; formula selection belongs to the active resource owner.
image_gen.py
Unified image generation entry point.
This script is the Path A API/proxy executor for generated images. Default
Generate checks design_spec.md §I / AI Image Acquisition Path before manifest
mode: only api / auto permits Path A; a missing or unknown value fails
closed and returns to Step 4 recovery. Quick Generate has no Design Spec: use
the explicit active-context path when supplied, otherwise auto selects the
A → B → C chain defined in
image-generator.md §7 without asking.
In either profile, host-native uses the host image tool directly and manual
uses the read-only Markdown sidecar.
python3 scripts/image_gen.py "A modern futuristic workspace"
python3 scripts/image_gen.py "Abstract tech background" --aspect_ratio 16:9 --image_size 4K
python3 scripts/image_gen.py "Concept car" -o projects/demo/images
python3 scripts/image_gen.py --list-backends
Backends are grouped into Core / Extended / Experimental tiers. Run python3 scripts/image_gen.py --list-backends for the current list.
Backend selection:
python3 scripts/image_gen.py "A cat" --backend openai
python3 scripts/image_gen.py "A cinematic portrait" --backend minimax
python3 scripts/image_gen.py "A product launch hero image" --backend qwen
python3 scripts/image_gen.py "科技感背景图" --backend zhipu
python3 scripts/image_gen.py "A product KV in cinematic style" --backend volcengine
Configuration sources:
- Current process environment variables
- First
.envfound in this order:- Current working directory
- Skill directory (e.g.
~/.agents/skills/ppt-master/.env) - Clone repo root
~/.ppt-master/.env
The active backend must always be selected explicitly via IMAGE_BACKEND.
Example .env:
IMAGE_BACKEND=openai
OPENAI_API_KEY=sk-xxx
OPENAI_MODEL=gpt-image-2
# Optional proxy
# OPENAI_BASE_URL=http://127.0.0.1:3000/v1
# OpenAI-compatible provider knobs:
# OPENAI_SIZE_PRESET=auto
# OPENAI_RESPONSE_FORMAT=auto
# OPENAI_QUALITY=auto
# Allowed values: png / jpeg / webp
# OPENAI_OUTPUT_FORMAT=png
# jpeg/webp only, 0-100
# OPENAI_OUTPUT_COMPRESSION=80
# gpt-image-2: auto / opaque
# OPENAI_BACKGROUND=auto
# auto / low
# OPENAI_MODERATION=auto
Example process environment:
export IMAGE_BACKEND=openai
export OPENAI_API_KEY=sk-xxx
export OPENAI_MODEL=gpt-image-2
export OPENAI_OUTPUT_FORMAT=png
Current process environment wins over .env.
OpenAI backend notes:
gpt-image-2is the default OpenAI model.- Requests are sent with plain
requests.post()to improve compatibility with OpenAI-compatible proxies that block the OpenAI SDK'shttpxtransport. - For
gpt-image-2,image_size=512pxmeans a low-quality draft preset, not a literal 512px edge. The model requires both edges to be multiples of 16px, a long:short ratio no greater than 3:1, and total pixels between 655,360 and 8,294,400. OPENAI_BACKGROUND=transparentis not supported bygpt-image-2; useautooropaque.- If
OPENAI_OUTPUT_FORMAT=jpegorwebp, generated files use.jpgor.webpextensions instead of.png. - OpenAI-compatible providers that reject OpenAI-specific fields can use
OPENAI_RESPONSE_FORMAT=omit,OPENAI_QUALITY=omit, andOPENAI_SIZE_PRESET=<preset>. Valid response formats areauto,b64_json,url, andomit; valid size presets areauto,legacy,gpt-image,gpt-image-2, anddall-e-2.
Example .env for Agnes AI through the OpenAI-compatible backend:
IMAGE_BACKEND=openai
OPENAI_API_KEY=your-agnes-key
OPENAI_MODEL=agnes-image-2.1-flash
OPENAI_BASE_URL=https://apihub.agnes-ai.com/v1
OPENAI_SIZE_PRESET=gpt-image-2
OPENAI_RESPONSE_FORMAT=omit
OPENAI_QUALITY=omit
Use provider-specific keys only (e.g. GEMINI_API_KEY, OPENAI_API_KEY). See .env.example in clone mode or ${SKILL_DIR}/.env.example in skill-install mode for the full list per backend.
IMAGE_API_KEY, IMAGE_MODEL, and IMAGE_BASE_URL are intentionally unsupported.
If you keep multiple providers in one .env or environment, IMAGE_BACKEND must explicitly select the active provider.
Recommendation:
- Default to the Core tier for routine PPT work
- Use Extended only when you need a specific model style
- Treat Experimental backends as opt-in
Example .env for MiniMax image backend:
IMAGE_BACKEND=minimax
MINIMAX_API_KEY=your-api-key
# Optional: override base URL (defaults to https://api.minimaxi.com, domestic China endpoint)
# Use https://api.minimax.io for overseas access
# MINIMAX_BASE_URL=https://api.minimax.io
# MINIMAX_MODEL=image-01
image_treat.py
Create a non-destructive PNG derivative from one bitmap already prepared under
<project_path>/images/. Use this only when a slide needs a baked bitmap effect;
crop, mask, rotation, mirror, opacity, shadow, scrim, outline, and overlap remain
native SVG/PPT treatments. This tool does not perform semantic background
removal: use slice_images.py --alpha for flat-color keys, an already prepared
RGBA asset or the active host image editor for a standalone cutout, and
image-generator.md §4.4 only for
registered subject/base layers.
python3 scripts/image_treat.py projects/demo hero.jpg \
--output hero_soft.png --brightness 0.9 --contrast 1.1 --blur 12
python3 scripts/image_treat.py projects/demo hero.jpg \
--output hero_duotone.png --duotone "#14213D" "#FCA311"
Supported operations are brightness, contrast, desaturation/grayscale, duotone, and Gaussian blur. They compose in a fixed order: brightness → contrast → tone treatment → blur. Desaturation, grayscale, and duotone are mutually exclusive. At least one option must produce a real change; animated or multi-frame sources are rejected rather than reduced to one frame.
Both input and output are bare filenames directly under images/; output must
be a new .png file. The tool keeps the EXIF-corrected display dimensions,
leaves any alpha mask unchanged, and never overwrites the source or an existing
derivative. If images/image_sources.json contains the source filename, the
new record inherits that legal provenance and records derived_from plus the
ordered treatments. Run analyze_images.py after all planned derivatives are
ready so the inventory reflects the files that SVG authoring will consume.
analyze_images.py
Analyze objective image-file facts in a project directory before writing the design spec or authoring SVG.
python3 scripts/analyze_images.py <project_path>/images
The tool does not resolve a canvas or recommend a left/right, top/bottom, or
other slide layout. Its atomic CSV records EXIF-corrected native dimensions and
AspectRatio, the objective aspect-ratio category, optional source
SourceDisplayRatio, format, actual transparent-pixel presence, usage count,
and bitmap/vector capability facts. An empty folder rewrites a header-only
report; unreadable supported files still refresh the report and produce a
non-zero exit.
Use this as the default factual inventory; it does not perform semantic image understanding or choose composition. Generate planning follows the Strategist's context-first boundary: source context, captions / alt text / titles, filenames, user notes, and existing resource records come first. Only an already-selected provided/web asset whose focal-safe crop, overlay contrast, or quiet region remains materially ambiguous may be inspected for that placement; this never reopens selection or provenance, never bulk-opens the image folder, and never restores routine readback of AI-generated images.
image_search.py
Zero-config web image search across openly-licensed providers. Sister tool to image_gen.py — used when the resource list row has Acquire Via: web.
python3 scripts/image_search.py "offshore wind farm" \
--filename cover_bg.jpg --slide 01_cover \
--orientation landscape -o projects/demo/images
For multiple web rows, --batch images/image_queries.json searches them concurrently (modest default, --concurrency N / IMAGE_SEARCH_CONCURRENCY to tune) instead of one call per row — the web sister of image_gen.py --manifest. Schema and status semantics: image-searcher.md §5.
Providers (Pexels / Pixabay are tried first when keyed; Openverse and Wikimedia are zero-config fallbacks):
| Provider | Config | Strength |
|---|---|---|
pexels |
recommended: PEXELS_API_KEY |
modern stock photography, people, workplace, lifestyle |
pixabay |
recommended: PIXABAY_API_KEY |
broad type coverage including photos and illustrations |
openverse |
zero-config | fallback aggregator: Wikimedia + Flickr + museums + rawpixel |
wikimedia |
zero-config | educational, scientific, geographic, historical |
Default search chain (when --provider is unset): configured Pexels, configured Pixabay, Openverse, then Wikimedia. Missing keyed credentials are silently skipped. Keyed providers broaden stock-photo coverage but are optional; zero-config providers remain valid.
image_search.py uses the same .env lookup order as image_gen.py, so skill installs can keep PEXELS_API_KEY / PIXABAY_API_KEY in ~/.ppt-master/.env.
Query guidance:
Keep the Design Spec §VIII Reference as the full visual/crop intent; write a separate concise provider query for this CLI. Start with the shortest phrase that preserves identity, but retain exact multi-word names and necessary disambiguators beyond four words.
| Case | Pattern |
|---|---|
| Generic stock concept | boardroom meeting |
| China-specific landmark | Precise official place/identity name plus necessary geography |
| Avoid | Negative prompt wording such as not tourist snapshot |
License filter:
- Default: search all providers with
cc0,pdm,pexels,pixabay,cc by,cc by-saallowed together. The chosen image may beno-attributionorattribution-required; Executor adds an inline credit only when needed. --strict-no-attributionrestricts the search tocc0,pdm,pexels,pixabay— useful for full-bleed hero images or templates that cannot host a credit element.
Pin a provider, refuse attribution, or override the manifest path:
# Pin Wikimedia
python3 scripts/image_search.py "Olympics opening ceremony" \
--filename event.jpg --provider wikimedia \
--orientation landscape -o projects/demo/images
# Strict mode — refuse CC BY / CC BY-SA
python3 scripts/image_search.py "abstract gradient" \
--filename hero.jpg --strict-no-attribution \
-o projects/demo/images
Suitability & manual replacement (a web top hit is metadata-relevant, not guaranteed visually right):
- By default only the best match is downloaded, plus a downscaled review copy at
images/.review/<stem>.jpg(the placed asset stays full-resolution). - For exact subjects (landmarks, people, companies, products), use
--require-termsor batchrequired_termsso visually plausible but wrong metadata is rejected before ranking. Example:--require-terms Chongqing --require-terms "Jiefangbei|Liberation Monument". Keep proper-name / geography anchors; do not broaden to generic terms likecanyon,stone pillar, orancient townjust to improve coverage. --save-candidates(with--max-candidates, default 4) keeps an opt-in escalation pool undercandidates/<stem>/; review it, then--promote candidate_03.jpg --filename <name>.jpg.--from-url <url> --filename <name>.jpgdownloads a user-chosen image URL and replaces the target (recordedlicense_tier: manual) — the model-agnostic manual path; works even without a multimodal model.
Full review / escalation flow: image-searcher.md §5.
Output:
- Image saved to the specified output directory (auto-converts webp → jpg via Pillow when the filename extension demands)
image_sources.jsonmanifest with full provenance (provider, license, license_tier, author, source URL, dimensions, attribution_text)- Manifest is idempotent on
filenameand written atomically; damaged existing provenance blocks replacement
Allowed licenses (default): CC0, Public Domain, Pexels License, Pixabay Content License, CC BY, CC BY-SA. Auto-rejected: CC BY-NC, CC BY-ND, CC BY-NC-SA, CC BY-NC-ND, all rights reserved, unknown.
The full role-level reference (intent → query translation, on-slide attribution contract) is in references/image-searcher.md.
gemini_watermark_remover.py
Remove Gemini watermark assets after manual download.
python3 scripts/gemini_watermark_remover.py <image_path>
python3 scripts/gemini_watermark_remover.py <image_path> -o output_path.png
python3 scripts/gemini_watermark_remover.py <image_path> -q
Notes:
- Requires
scripts/assets/bg_48.pngandscripts/assets/bg_96.png - Best used after downloading “full size” Gemini images
Dependencies:
pip install Pillow numpy