Files
imagepipeline/RECIPES.md
Frank Schwenk 9b6c80da42 feat: add aichelberg-indians pipeline and rebuild RECIPES.md
Replace the verbose recipe catalog with a compact agent reference (modules,
ImageMagick ops, G'MIC commands, combines) and add the Indians Aichelberg
tone-mapping pipeline with gmic variants and xcf export.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-13 10:15:28 +02:00

311 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RECIPES.md
Reference for agents building `pipelines/<name>.py`. Not executable — expand to `p.step(...)` calls.
---
## Rules
1. **combine** = one output image. Layers **bottom → top** (first line = background).
2. **composite** takes exactly **2** inputs. For 3+ layers: `composite(A,B)``composite(mid, C)` → …
3. **rembg** once per pipeline — reuse the same step ref in every combine.
4. **G'MIC in Python:** `command="-filter args"` (leading `-`). Multi-frame output: framework keeps frame `000001`.
5. **Colors:** hex (`#RRGGBB`) for `imagemagick_fill` / `color_to_alpha`. G'MIC color slots: `R,G,B` ints 0255 from hex bytes.
6. **Placeholders** at pipeline top: `COLOR1`, `COLOR2`, `R1,G1,B1`, `R2,G2,B2`, `ALPHA1` (default 160), `ALPHA2` (default 110), `STYLE`, `MAX_EDGE`, `PROMPT`, `MODEL`, `TEMPLATE_IMAGE`, `YELLOW`.
7. **xcf_stack:** separate step, not a combine layer. `inputs=[...]` bottom → top; include `"input"` for originals. Needs `gimp` on PATH.
8. Lines starting with `#` in user prompts = human notes — ignore.
**Hex → RGB:** `#244a89``36,74,137`. Strip `#RRGGBBAA` alpha for G'MIC tuples.
---
## Layer lines (example)
One combine, bottom → top. Other phrases: `grayscale` / `original as greyscale`, `white`/`black background`, `original with gmic: ID`, `#COLOR to alpha`, `resize max edge N`, `crop square`, `darktable style STYLE`, `openrouter edit …`, `xcf stack layers: a, b, c`.
```text
demo-combine
gradient background COLOR1 COLOR2 radial
original
rembg with gmic: gmic-drop-shadow
make layer 5% bigger then crop to original size
rembg
```
---
## Modules
| name | parameters (defaults) | notes |
|------|----------------------|-------|
| `rembg` | `model`=`birefnet-general`, `alpha_matting`=`True` | output `.png` |
| `gmic` | `command` (required) | timeout 300s |
| `gmic_grayscale` | `command`=`-to_gray` | |
| `composite` | `mode`=`over`, `output_ext`=`.png`, `foreground_opacity`=`1.0` | 2 inputs: bg, fg |
| `imagemagick_fill` | `color1`, `color2`=`""`, `gradient`=`False`, `radial`=`False`, `angle`=`None` | sized to input |
| `imagemagick_grayscale` | `colorspace`=`Gray` | |
| `imagemagick_resize` | `max_edge`=`2000` | no upscale |
| `imagemagick_scale_crop` | `scale`=`1.05` | center crop after scale |
| `color_to_alpha` | `color` (required), `fuzz`=`0.0` | output `.png` |
| `crop_square` | — | center square crop |
| `darktable_style` | `style` (required), `style_overwrite`=`True`, `out_ext`=`""`, `config_dir`=`~/.config/darktable` | jpeg/png only |
| `xcf_stack` | `skip_missing`=`False`, `timeout`=`120` | multi-input step |
| `openrouter_edit` | `prompt`, `model`, `strength`=`0.3`, `template_image`=`None`, `api_key_env`=`OPENROUTER_API_KEY` + AI common | needs `.env` |
| `ai_exposure` | `strength`=`1.0` + AI common | `[ai]` extra |
| `ai_tone_map` | `checkpoint`=`""`, `strength`=`1.0`, `net_input_size`=`256` + AI common | `[ai]` extra |
| `comfy_flux_edit` | `prompt`, `denoise`=`0.35`, `seed`=`-1`, `server_url`, `workflow_path`, `poll_interval`=`2.0` + AI common | local ComfyUI |
**AI common** (`ai_exposure`, `ai_tone_map`, `openrouter_edit`, `comfy_flux_edit`): `skip_existing`=`True`, `max_edge`=`2048`, `device`=`cpu`.
---
## ImageMagick operations
Used via modules above — not free-form CLI in pipelines. `W×H` = size of reference input image.
| id | module | operation |
|----|--------|-----------|
| `im-solid` | `imagemagick_fill` | `-size W×H xc:COLOR` |
| `im-gradient-linear` | `imagemagick_fill` | `-size W×H -define gradient:angle=N gradient:COLOR1-COLOR2` |
| `im-gradient-radial` | `imagemagick_fill` | `-size W×H radial-gradient:COLOR1-COLOR2` |
| `im-grayscale` | `imagemagick_grayscale` | `-colorspace Gray` |
| `im-resize-max-edge` | `imagemagick_resize` | `-auto-orient -resize {N}x{N}>` (no upscale) |
| `im-scale-crop` | `imagemagick_scale_crop` | `-resize {w×scale}x{h×scale}! -gravity Center -crop w×h+0+0 +repage` |
| `im-crop-square` | `crop_square` | `-auto-orient -crop S×S+X+Y +repage` (center square) |
| `im-color-to-alpha` | `color_to_alpha` | `-alpha on [-fuzz N%] -transparent COLOR` |
| `im-composite-over` | `composite` | `(bg) (fg) -compose over -composite` |
| `im-composite-multiply` | `composite` | `mode=multiply` |
| `im-composite-screen` | `composite` | `mode=screen` |
| `im-composite-opacity` | `composite` | fg: `-channel A -evaluate multiply {0..1}` then compose |
**Layer line → id:** `COLOR background``im-solid`; `gradient … 45 degree``im-gradient-linear`; `gradient … radial``im-gradient-radial`; `grayscale``im-grayscale`; `resize max edge N``im-resize-max-edge`; `make layer 5% bigger …``im-scale-crop` (`scale=1.05`); `crop square``im-crop-square`; `COLOR to alpha``im-color-to-alpha`.
---
## G'MIC commands
Python string = `-` + filter + args. Placeholders `R1,G1,B1` / `R2,G2,B2` from `COLOR1` / `COLOR2`.
| id | command |
|----|---------|
| `gmic-tone-mapping` | `fx_map_tones 0.5,0.7,0.1,30,0` |
| `gmic-grayscale` | `to_gray` (via `gmic_grayscale`) |
| `gmic-feltpen` | `fx_feltpen 300,50,1,0.1,20,5` |
| `gmic-edges` | `fx_edges 0,15,0` |
| `gmic-charred-plastic` | `fx_charred_plastic 1,10,40,1,10,0,0,2,6,5,20,0,11` |
| `gmic-neon` | `fx_neon 0,1,1,1,1,0,0.45,40,60,0,1,1.15,2,3,0,3,20,0.4,0.1,1.5,5,0.2,0.1,1,2,1,0,0,1,1,0,50,50` |
| `gmic-cutout` | `fx_cutout 4,0.5,4,1` |
| `gmic-poster` | `fx_poster_hope 0,3` |
| `gmic-color-abstraction` | `fx_color_abstraction 1,10,0.2` |
| `gmic-huffman-glitches` | `fx_huffman_glitches 30,0,25,0,0,0,0,0,50,50` |
| `gmic-queryprimary` | `afre_queryprimary 1,1` |
| `gmic-local-similarity` | `local_similarity_mask 50,50,50,128,4,10` |
| `gmic-luma-invert` | `Lylejk_Luma_Invert 1,0` |
| `gmic-crayongraffiti` | `fx_crayongraffiti2 300,50,1,0.4,12,1,2,2,0` |
| `gmic-anaglyph` | `fx_stereo_to_anaglyph 2,0` |
| `gmic-stereo` | `gcd_stereo_img 0,0,2.028,1,1.714,3.06,4,1,0` |
| `gmic-drop-shadow` | `fx_drop_shadow3d 0,0,0,10,1,1,2,0.5,R2,G2,B2,200,0` |
| `gmic-jpr-smooth` | `jpr_gradient_smooth 0,1.5` |
| `gmic-bokeh` | `fx_bokeh 3,5,0,30,8,4,0.3,0.2,R1,G1,B1,ALPHA1,0.7,30,20,20,1,2,R2,G2,B2,ALPHA2,0.15` |
| `gmic-bwrecolor` | `fx_bwrecolorize 0,0,0,0,0,1,0,2,R2,G2,B2,255,R1,G1,B1,255,158,137,189,255,224,191,228,255,R1,G1,B1,0,255,255,255,255,255,255,255,255,255,R1,G1,B1,0,255` |
| `gmic-custom-gradient-a` | `fx_custom_gradient 0,0,0,1,2,1,0,128,100,100,2,0,1,0,"",1,0,R1,G1,B1,0,255,R2,G2,B2,255,255,255,0,255,255,255,255,255,0,255,255,255,0,255,0,255,0,0,255,255,128,128,128,255,255,0,255,255,0,0,0,0` |
| `gmic-custom-gradient-b` | `fx_custom_gradient 0,0,0,1,2,1,0,128,100,100,2,0,1,0,"",1,0,R2,G2,B2,255,R1,G1,B1,0,255,255,255,0,255,255,255,255,255,0,255,255,255,0,255,0,255,0,0,255,255,128,128,128,255,255,0,255,255,0,0,0,0` |
**Typical input:** `original with gmic:` = on `"input"`; `rembg with gmic:` = on rembg step.
---
## Combines
Format: recipe id, then layers bottom → top. `→` = chained composites.
### 2 layers
```text
colorsplash
original as greyscale
rembg
rembg-white-bg
white background
rembg
rembg-black-bg
black background
rembg
rembg-gradient-45
gradient background COLOR1 COLOR2 45 degree
rembg
rembg-radial-2colors
gradient background COLOR1 COLOR2 radial
rembg
rembg-custom-gradient-a
rembg with gmic: gmic-custom-gradient-a
rembg
rembg-custom-gradient-b
rembg with gmic: gmic-custom-gradient-b
rembg
gmic-feltpen-rembg
rembg with gmic: gmic-feltpen
rembg
gmic-edges-rembg
rembg with gmic: gmic-edges
rembg
gmic-charred-plastic-rembg
rembg with gmic: gmic-charred-plastic
rembg
gmic-neon-rembg
rembg with gmic: gmic-neon
rembg
```
### 3 layers
```text
original-stereo-rembg
original
rembg with gmic: gmic-stereo
rembg
→ composite(original, rembg_stereo) → composite(_, rembg)
original-drop-shadow-rembg
original
rembg with gmic: gmic-drop-shadow
rembg
original-bwrecolor-rembg
original
rembg with gmic: gmic-bwrecolor
rembg
→ first composite: foreground_opacity=0.5
original-jpr-smooth-rembg
original
rembg with gmic: gmic-jpr-smooth
make layer 5% bigger then crop to original size
rembg
→ gmic → scale_crop on middle layer, then 3-layer chain
bokeh-oktagon
original
original with gmic: gmic-bokeh
rembg
color-bg-drop-shadow-rembg
COLOR1 background
rembg with gmic: gmic-drop-shadow
rembg
yellow-bg-drop-shadow-rembg
YELLOW background
rembg with gmic: gmic-drop-shadow
rembg
gmic-cutout-rembg
original
original with gmic: gmic-cutout
rembg
gmic-poster-rembg
original
original with gmic: gmic-poster
rembg
gmic-color-abstraction-rembg
original
original with gmic: gmic-color-abstraction
rembg
gmic-huffman-glitches-rembg
original
original with gmic: gmic-huffman-glitches
rembg
gmic-queryprimary-rembg
original
original with gmic: gmic-queryprimary
rembg
gmic-local-similarity-rembg
original
original with gmic: gmic-local-similarity
rembg
gmic-luma-invert-rembg
original
original with gmic: gmic-luma-invert
rembg
gmic-crayongraffiti-rembg
original
original with gmic: gmic-crayongraffiti
rembg
gmic-anaglyph-rembg
original
original with gmic: gmic-anaglyph
rembg
```
### 4 layers (middle pipeline)
```text
original-stereo-black-alpha-rembg
original
rembg with gmic: gmic-stereo
#000000 to alpha
rembg
original-jpr-smooth-grey-alpha-rembg
original
rembg with gmic: gmic-jpr-smooth
#7f7f7f to alpha
make layer 5% bigger then crop to original size
rembg
```
### Pipeline chain (not a combine)
```text
colorsplash-watermark
colorsplash # expand combine first
darktable style STYLE
```
---
## Agent example
Request: *pipeline with colorsplash + rembg-radial-2colors, COLOR1=#244a89, COLOR2=#c22518*
```python
rembg_out = p.step("rembg", inputs="input")
grayscale = p.step("gmic_grayscale", inputs="input")
gradient_bg = p.step("imagemagick_fill", inputs="input", color1=COLOR1, color2=COLOR2, gradient=True, radial=True)
p.step("composite", inputs=[grayscale, rembg_out]) # colorsplash
p.step("composite", inputs=[gradient_bg, rembg_out]) # rembg-radial-2colors
```
3-layer combine (drop shadow on original):
```python
rembg_shadow = p.step("gmic", inputs=rembg_out, command=GMIC_DROP_SHADOW)
mid = p.step("composite", inputs=["input", rembg_shadow])
p.step("composite", inputs=[mid, rembg_out])
```
xcf export:
```python
p.step("xcf_stack", inputs=["input", rembg_out, grayscale, composite_colorsplash, ...])
```