# RECIPES.md Reference for agents building `pipelines/.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, ...]) ```