Image-to-Embroidery Visual Search Engine
Upload any garment photograph. Get the matching .EMB embroidery design — in under 50 ms.
EMBFinder bridges the domain gap between photographic garment images and technical embroidery renders. It combines a Go-native backend with a Python AI embedding service to achieve production-grade search accuracy across large design libraries.
| Metric | Result |
|---|---|
| Accuracy @ Top-1 | 93.8% |
| Accuracy @ Top-3 | 99.0% |
| Accuracy @ Top-5 | 100.0% |
| Accuracy @ Top-10 | 100.0% |
| Search Latency | < 50 ms |
| Model | ViT-L-14 / OpenAI (CUDA) |
┌─────────────────────────────────────────────────────────────┐
│ Client (Browser) │
└────────────────────────┬────────────────────────────────────┘
│ HTTP
┌────────────────────────▼────────────────────────────────────┐
│ go-server (port 8765) │
│ │
│ handlers.go ── HTTP routes & multipart parsing │
│ indexer.go ── background EMB walker + dual-embedding │
│ search.go ── parallel sharded cosine-similarity │
│ db.go ── SQLite WAL (render + sidecar vectors) │
│ watcher.go ── fsnotify real-time change detection │
└───────┬──────────────────────────────────┬──────────────────┘
│ HTTP (port 8766) │ HTTP (port 8767)
┌───────▼───────────────┐ ┌───────────▼────────────────┐
│ embedder (Python) │ │ emb-engine (Python) │
│ │ │ │
│ CLIP ViT-L-14/OpenAI │ │ OLE2 extraction │
│ Multi-crop query │ │ PyEmbroidery stitch render │
│ Augmented indexing │ │ Placeholder fallback │
│ Embroidery region │ │ │
│ detection (OpenCV) │ │ │
└───────────────────────┘ └────────────────────────────┘
Every EMB file is stored with two CLIP vectors:
| Vector | Source | Purpose |
|---|---|---|
| Render embedding | Flat PNG render of the EMB | Structural shape matching |
| Sidecar embedding | Averaged augmented views of all paired garment photos | Photo-domain matching |
At search time: score = max(render_score, sidecar_score)
This eliminates the domain gap between photographic query images and synthetic technical renders.
- Independent Folder Management — Manage multiple embroidery collections independently. Only indexed folders are searched.
- Auto-Tuned Performance — CPU/RAM-aware worker pools and GC tuning (low-ram, balanced, high-memory profiles).
- Real-Time File Watching — Automatically re-index designs when files change (create, modify, rename, delete).
- Dual-Vector Search — Combines structural shape matching (render) with photo-domain matching (sidecar photos).
- Stall Recovery — Automatic detection and recovery from stuck indexing processes (2-minute threshold).
- Queue-Based Indexing — Multiple scan jobs can be queued and processed sequentially without blocking the UI.
- Per-Folder Statistics — Track indexing progress, total files, and indexed count per folder.
- Modern UI — Dark-mode interface with glass morphism effects, drive selection checkboxes, and responsive design.
- Zero Runtime Dependencies — Static Go binary works on any Linux with no external deps. Python services containerized.
Pre-built binaries are available on the Releases page.
| Platform | Download | Notes |
|---|---|---|
| Linux x86-64 | embfinder_vX.Y.Z_linux_amd64.tar.gz |
Static binary, no dependencies |
| Linux ARM64 | embfinder_vX.Y.Z_linux_arm64.tar.gz |
Raspberry Pi, AWS Graviton |
| Linux (Debian/Ubuntu) | embfinder_vX.Y.Z_linux_amd64.deb |
Installs systemd service |
| Linux (Fedora/RHEL) | embfinder_vX.Y.Z_linux_amd64.rpm |
|
| Windows 10/11 | embfinder_vX.Y.Z_windows_amd64.zip |
Extract and run |
| macOS Intel | embfinder_vX.Y.Z_darwin_amd64.tar.gz |
|
| macOS Apple Silicon | embfinder_vX.Y.Z_darwin_arm64.tar.gz |
M1 / M2 / M3 |
The Go binary alone is not enough. You also need the Python services (AI embedder + EMB renderer). Start them with Docker Compose — see Quick Start.
img_emb_finder/
├── go-server/ Go backend (binary: embfinder)
│ ├── main_desktop.go Desktop build — Wails native window (!headless tag)
│ ├── main_headless.go Server build — pure HTTP, opens browser (headless tag)
│ ├── server.go Shared startup: startCore(), route registration, background goroutines
│ ├── config.go Environment config, auto-tuned worker pools, GC tuning
│ ├── handlers.go HTTP route handlers — search, indexing, folder management
│ ├── indexer.go Background EMB walker, dual-embedding, stall recovery
│ ├── search.go Parallel sharded cosine similarity, multi-crop scoring
│ ├── db.go SQLite WAL, per-folder tracking, content-hash deduplication
│ ├── drives.go Drive detection + selected drive state management
│ ├── clip.go Vector type definitions + distance calculations
│ ├── watcher.go fsnotify recursive monitoring, real-time re-index
│ └── ui/ Modern embedded web UI (HTML + CSS + JS, no build step)
│ ├── index.html Responsive dark-mode interface
│ ├── style.css Glass morphism, responsive grid, accessibility
│ ├── api.js Network layer — all fetch() calls to backend
│ ├── controllers.js UI logic: drive selection, folder mgmt, indexing
│ └── app.js Event orchestration, controller initialization
├── embedder/ AI embedding service (Python / FastAPI / CUDA)
│ ├── main.py ViT-L-14 multi-crop embeddings, augmented views
│ └── requirements.txt torch, fastapi, open-clip
├── emb-engine/ EMB rendering service (Python / Flask)
│ ├── server.py OLE2 extraction, PyEmbroidery, TrueSizer integration
│ ├── emb_renderer.py Binary render extraction, fallback chain
│ └── requirements.txt flask, pillow, olefile, pyembroidery
├── tests/ Validation suite (dev mode only)
│ ├── lib.py Shared utilities (single source of truth)
│ ├── quick_test.py Fast per-query accuracy check
│ └── mega_test.py Full-dataset evaluation + false-positive analysis
├── scripts/
│ ├── postinstall.sh systemd service registration (deb/rpm)
│ └── preremove.sh systemd service cleanup before uninstall
├── .github/workflows/
│ ├── ci.yml Build + syntax check on every push / PR
│ └── release.yml Cross-platform release on tag push
├── Makefile Developer shortcuts (make dev, make build, make release)
├── docker-compose.yml
├── .env.example
├── .gitignore Includes .logs/ to exclude binary artifacts
└── .goreleaser.yml GoReleaser v2 — produces 7 platform archives + .deb/.rpm
The simplest way to run EMBFinder. Handles all dependencies — Go, Python, CUDA.
Prerequisites: Docker Engine and Docker Compose.
# 1. Configure your embroidery library path
cp .env.example .env
# Edit .env — set EMB_LIB to your embroidery folder
# 2. Build and start all services
docker-compose up --buildThe first build downloads the CLIP ViT-L-14 model (~900 MB). Subsequent starts are instant.
Open the UI at http://127.0.0.1:8765
| Component | Minimum |
|---|---|
| OS | Linux (Ubuntu 20.04+) / macOS 12+ / Windows 10+ |
| Go | 1.22+ |
| Python | 3.10+ |
| CUDA | 11.8+ (optional, CPU fallback available) |
| RAM | 8 GB (16 GB recommended) |
| Disk | 2 GB (for CLIP model cache) |
cd emb-engine
pip install -r requirements.txt
python3 server.pycd embedder
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8766 --workers 1The embedder loads ViT-L-14/OpenAI on startup. GPU is detected automatically.
cd go-server
# Development (Wails desktop window + hot reload)
go run --tags dev .
# Headless HTTP server (opens browser, no native window)
go build -tags headless -o embfinder .
./embfinder
# Or use the Makefile from the repo root
make dev # starts all three services
make build # headless Linux binary → dist/embfindercurl http://localhost:8765/ # Go backend
curl http://localhost:8766/health # AI embedder
curl http://localhost:8767/health # EMB engine- Open http://127.0.0.1:8765 in a browser.
- Select the folder containing your
.EMBfiles. - Click Scan — the indexer walks the folder, renders each EMB, and generates dual embeddings.
- Search by uploading any garment photo.
The indexer also picks up sidecar images automatically: if s (1).EMB exists alongside s (1).jpg, both are embedded and averaged into a single robust sidecar vector.
# Quick accuracy check (reuses existing index)
python3 tests/quick_test.py --skip-index
# Full evaluation: re-index + comprehensive report
python3 tests/mega_test.py
# Target a different host or dataset
python3 tests/mega_test.py --host http://192.168.1.10:8765 --data_dir /mnt/emb_libBoth scripts write a JSON report to /tmp/embfinder_mega_test.json.
Or via Makefile:
make test # quick_test.py --skip-index
make test-full # mega_test.py (re-indexes library)# Headless binary (static, CGO_ENABLED=0 — works on any Linux with no deps)
make build
# → dist/embfinder (~9.5 MB)
# Cross-compile all platforms locally (requires goreleaser)
make build-all
# → go-server/dist/embfinder_*_linux_amd64.tar.gz
# → go-server/dist/embfinder_*_windows_amd64.zip
# → go-server/dist/embfinder_*_darwin_arm64.tar.gz
# → go-server/dist/embfinder_*_linux_amd64.deb (+ .rpm)# Tag a version — GitHub Actions builds everything automatically
make release VERSION=v1.0.0
# Equivalent to:
git tag v1.0.0 && git push origin v1.0.0The release.yml workflow runs on tag push and:
- Tests:
go vet+ headless build check - GoReleaser: produces all 7 platform archives +
.deb/.rpm→ GitHub Release - Docker: pushes
go-server,embedder,emb-engineimages toghcr.io
| Command | Build Tag | Wails Window | CGO Required | Use Case |
|---|---|---|---|---|
go run --tags dev . |
!headless |
Yes | Yes | Local desktop development |
go build . |
!headless |
Yes | Yes | Desktop app build |
go build -tags headless . |
headless |
No (browser) | No | Server, Docker, CI, releases |
| Variable | Default | Description |
|---|---|---|
PORT |
8765 |
Go backend HTTP port |
EMBEDDER_PORT |
8766 |
Python embedder port |
EMBEDDER_URL |
auto | Full embedder URL (overrides port) |
EMB_ENGINE_URL |
http://localhost:8767 |
EMB engine URL |
HEADLESS |
0 |
1 = run without Wails desktop window |
DB_PATH |
data/embfinder.db |
SQLite database path |
EMBFIND_DATA_DIR |
— | Dedicated directory for all EMBFinder data |
EMBFIND_EXTRA_DRIVES |
— | Extra scan paths (semicolon-separated) |
MAX_WORKERS |
auto | Manual override for indexing parallelism |
CLIP_MODEL |
ViT-L-14 |
CLIP model variant |
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Web UI |
POST |
/api/search |
Search by image (multipart/form-data, field: file) |
GET |
/api/preview/{id} |
PNG render of a design (1-week cache) |
GET |
/api/thumbnail/{id} |
Sidecar garment photo or render fallback |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/index/start |
Trigger scan: {"paths": [...], "force": bool} |
POST |
/api/index/stop-all |
Force-stop indexing and clear job queue |
GET |
/api/index/toggle |
Pause / resume background auto-sync |
GET |
/api/index/state/stream |
SSE stream of live indexing progress |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/drives |
List detected drives with selection state |
POST |
/api/drives/select |
Set scan folders: {"paths": [...]} |
GET |
/api/folders |
Per-folder statistics and status |
POST |
/api/folders/rescan |
Immediately rescan a specific folder |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/latest |
Latest 50 indexed designs |
GET |
/api/browse |
Paginated library: ?page=1&q=rose |
POST |
/api/emb-info |
Extract stitch/color/trim metadata |
POST |
/api/open-file |
Open design folder in OS file manager |
POST |
/api/open-truesizer |
Open design in TrueSizer GUI |
| Method | Endpoint | Description |
|---|---|---|
DELETE |
/api/clear |
Wipe database and memory index |
Contributions are welcome. Please follow the process below to keep the codebase clean and reviewable.
- Search existing issues to avoid duplicates.
- Open a new issue using the Bug Report template.
- Include:
- OS and version
- Go / Python versions (
go version,python3 --version) - Exact error output or log lines
- Steps to reproduce
- Open an issue using the Feature Request template.
- Describe the use case, not just the implementation idea.
- Link any relevant research papers or similar tools if applicable.
# Fork and clone
git clone https://github.com/your-fork/img_emb_finder.git
cd img_emb_finder
# Create a focused branch
git checkout -b fix/sidecar-path-matching
# Make your changes, then verify all build modes
cd go-server
go build ./... # desktop build (needs CGO)
CGO_ENABLED=0 go build -tags headless . # headless build (static)
# Syntax-check Python
python3 -m py_compile embedder/main.py emb-engine/server.py tests/lib.py
# Run the test suite
make test
# Commit with a conventional message
git commit -m "fix: resolve sidecar path matching for uppercase extensions"
# Push and open a PR against main
git push origin fix/sidecar-path-matchingPR checklist:
-
go build ./...passes (desktop build) -
CGO_ENABLED=0 go build -tags headless .passes (release build) - Python files pass
python3 -m py_compile <file> -
make testpasses or accuracy does not regress - No new dependencies added without justification in the PR description
- Changes documented in the relevant
README.md
| Language | Standard |
|---|---|
| Go | gofmt — enforced, no exceptions |
| Python | PEP 8 — 100-char line limit |
| Commits | Conventional Commits (fix:, feat:, chore:, docs:) |
MIT — see LICENSE.
Third Party Notice: EMBFinder can interface with proprietary embroidery engines (Wilcom EmbroideryStudio, etc.). This project does not distribute or include any proprietary software. You are responsible for obtaining valid licences for any third-party engine you connect.