Flat logo raster → RGB vector PDF tracer.
Drop a JPEG/PNG, get a clean PDF ready for print and Illustrator.
Pipeline: upscale (auto) → background detection (always kept) → unified auto palette → remap → VTracer spline → PDF
cd /srv/hermes/projects/logotrace
# Development environment (the deployed one is built by deploy/live-venv.sh)
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
# CLI
PYTHONPATH=. .venv/bin/python -m src.cli input/logo.jpg -o output/logo.pdf
# CLI with upscale off (escape hatch)
PYTHONPATH=. LOGOTRACE_UPSCALE=off .venv/bin/python -m src.cli input/logo.jpg -o output/logo.pdf
# API — the deployed instance listens on 127.0.0.1:8302 behind nginx
curl -s -F file=@input/logo.jpg -F palette=auto http://127.0.0.1:8302/vectorize -o out.pdfWeb UI: https://trace.idealabs.dev/ — paste/drop, Auto|1–8 colors, canvas preview + zoom/pan.
Deploy note: the service runs on mainframe as site-trace (port 8302); the live tree is
/srv/sites/trace and is only ever written by the hosting script. One command does the whole
deploy — mirror the tree, rebuild the virtualenv (sync deletes what the repository does not
contain), restart, check health:
bash deploy/deploy.shRaster (JPEG/PNG)
│
├─ [src/pipeline.py] upscale preprocess (default: auto toward 3072px cap)
│ └─ Lanczos resize, effective factor ≤ 2x, skip if < 1.05x,
│ pixel-unit VTracer thresholds scaled by effective factor
│
├─ [src/colors.py] unified auto palette (ADR-22/23)
│ ├─ estimate_background() bg detected, ALWAYS kept as bottom layer
│ ├─ mass filter clusters ≥ 3% of dominant
│ ├─ weighted-HSL merge w_L=2 w_S=1 w_H=1, thr 0.18 — never hue-only
│ ├─ N = survivors, 1..8 (auto mode)
│ └─ gradient crush mass anchor; cannot drop mass colors;
│ bimodal guard kept as safety net
│
├─ [src/preprocess.py] brand-color remap
│ └─ remap_to_palette() snap pixels to measured colors (bg incl.)
│
├─ [src/tracer_vtracer.py] subprocess → VTracer 0.6.4
│ └─ spline mode, color, mild speckle
│
├─ [src/postprocess.py] svgo optimize
│
├─ [src/geometry.py] path simplify (off by default, experimental — ADR-14)
│
├─ [src/pdf_export.py] SVG → PDF (rsvg-convert / cairosvg)
│
├─ [src/verify.py] self-check: raster output, assert non-blank
│
├─ [src/pipeline.py] orchestrator: vectorize_file / vectorize_bytes
│
├─ [src/debug_save.py] persist input/ui_* + output/ui_* + last.*
│
├─ [src/api.py] FastAPI: POST /vectorize, GET /health, static UI
├─ [src/cli.py] Typer: --colors, --colors-mode, --geom, --format
└─ [src/config.py] VTracer binary path, thresholds
palette |
Mode | Behavior |
|---|---|---|
auto |
auto-N | bg + N inks, N = mass/HSL survivors, 1..8 (ADR-23) |
1–16 |
exact K | bg + exactly K inks by mass, no crush |
Background occupies a reserved slot outside the ink limit (ADR-22).
Response headers: X-LogoTrace-Colors, X-LogoTrace-Colors-Mode.
--geom off (default) keeps raw VTracer splines. basic/strict are
experimental and may facet curves. A separate geometry_fit refinement pass
was built, benchmarked and removed (no visible benefit — ADR-21).
python -m src.eval input/ --colors 4Canonical sample set: input/sample_01..sample_11. Metrics: area-weighted
IoU (background is a first-class mask class), Chamfer on color-transition
edges, nodes, SVG size. The log prints both iou_mean and iou_aw;
iou_aw is the quality aggregate — see
docs/EVAL-BASELINE.md. Pixel metrics are blind
to smoothness and palette semantics — owner visual review is part of
acceptance for palette/curve changes.
logotrace/
bin/vtracer # bundled 0.6.4 Linux x86_64
src/
api.py, cli.py, config.py
colors.py # unified auto palette (ADR-22/23)
preprocess.py, tracer_vtracer.py, pipeline.py
pdf_export.py, postprocess.py, geometry.py
verify.py, debug_save.py, disks.py
eval.py # benchmark harness
static/ # Web UI (html/css/js + pdf.js)
tests/ # pytest
docs/ # STATUS, PRODUCT, PLAN, DECISIONS, PITFALLS + SPEC, EVAL-BASELINE, QA-NOTES
deploy/ # live-venv.sh (builds the service venv in the live tree) + nginx snippet
input/ # sample_01..sample_11 + ui_* debug dumps
output/ # gitignored PDFs/SVGs
The live copy lives at /srv/sites/trace and belongs to site-trace; only hermes-site-ctl sync
writes there. The service virtualenv is built inside it by deploy/deploy.sh, because the service
account cannot read /srv/hermes — see docs/PITFALLS.md.
# auto (default): auto-N palette (ADR-23)
python -m src.cli input.jpg -o out.pdf
# manual exact K: no crush — dual gray survives
python -m src.cli input/sample_05.jpg -o out.pdf -c 2 --colors-mode exact
# debug SVG path geometry
python -m src.cli input.jpg -o out.svg --format svgPOST /vectorize
file=@logo.jpg (multipart, required)
palette=auto|1-16 (default auto; UI exposes 1-8)
format=pdf|svg (default pdf)
GET /health
GET / (Web UI)
AGENTS.md— working agreement: roles, levels, git, verificationdocs/STATUS.md— current state, what is broken, next actiondocs/PRODUCT.md— why it exists, who it is for, what it must dodocs/PLAN.md— the migration to mainframe, parked items, risksdocs/DECISIONS.md— architecture decision records (ADR-1…26)docs/PITFALLS.md— what already broke and how not to repeat itdocs/SPEC.md— product spec, user stories, success criteriadocs/EVAL-BASELINE.md— benchmark baselines and metric historydocs/QA-NOTES.md— manual QA rounds, sample resultsdocs/RESEARCH.md— tracer selection researchdocs/ai-preflight-vision-qa.md— AI vision QA proposal