9b6c80da42
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>
311 lines
10 KiB
Markdown
311 lines
10 KiB
Markdown
# 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 0–255 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, ...])
|
||
```
|