rockierocker1993/runpod-worker-image-svg

0

stars

3

commits

Python

primary language

May 18, 2026

updated

README

RunPod Worker Image-to-SVG (OmniSVG)

RunPod serverless worker untuk konversi image → SVG menggunakan OmniSVG (Qwen2.5-VL backbone) dengan dukungan GPU.

Pipeline ini hasil migrasi dari worker Real-ESRGAN upscaler — semua mekanisme storage / webhook / database tetap sama, hanya stage inti upscale yang diganti menjadi image-to-svg generation.

📋 Fitur

  • Flexible Input Storage: S3 atau RunPod Network Volume
  • Flexible Output Storage: Cloudflare Images (CDN, SVG supported) atau Network Volume (.svg file)
  • ✅ Generate SVG dari gambar input menggunakan OmniSVG (3B / 4B / 8B)
  • ✅ Multi-candidate generation dengan kontrol temperature, top_p, top_k, repetition_penalty
  • ✅ Auto background replacement (white background) untuk hasil yang lebih bersih
  • ✅ Auto-delete input image setelah generate (opsional)
  • ✅ Warming-up worker tanpa memproses gambar (image: "warming-up")
  • ✅ Simpan metadata ke database PostgreSQL (opsional)
  • ✅ Webhook callback async untuk notifikasi status (success/error)
  • ✅ Models di-load dari RunPod Network Volume atau di-download dari HuggingFace
  • ✅ GPU acceleration (CUDA 12.1) dengan BF16/FP16

🖥️ System Requirements

Hardware

Model sizeGPU memoryRecommended GPU
OmniSVG-3B~17 GBRTX 3090 / A5000
OmniSVG1.1_4B~17 GBRTX 3090 / A5000
OmniSVG1.1_8B~26 GBA6000 / A100 40GB
  • RAM: 16 GB+
  • Storage: ~25 GB untuk Docker image + model weights
  • CUDA: 12.1 (di-bundle di base image)

Cloud (RunPod)

  • GPU Instance: A5000 / A6000 / A100 (sesuai model size)
  • Disk Space: 25 GB minimum
  • Network: Akses ke S3 endpoint (input) dan Cloudflare API (output)

📦 Dependencies

Python Packages

runpod
transformers==4.51.3
qwen-vl-utils==0.0.11
huggingface-hub
accelerate
einops==0.4.1
PyYAML==6.0.2
Pillow
numpy<2.0.0
CairoSVG==2.7.1
shapely==2.0.7
boto3
requests
sqlalchemy
psycopg2-binary

CUDA Libraries

  • PyTorch 2.3.0 (CUDA 12.1)
  • torchvision 0.18.0

System Libraries

  • libcairo2 / libcairo2-dev (untuk CairoSVG)
  • libgl1, libglib2.0-0 (untuk Pillow / OpenCV)
  • git (untuk clone OmniSVG repo di build time)

📁 Project Structure

runpod-worker-image-svg/
├── main.py                  # RunPod handler & pipeline flow
├── svg_generator.py         # OmniSVG image-to-SVG inference wrapper
├── db/                      # Database module
│   ├── __init__.py
│   ├── database.py
│   ├── models.py            # GeneratedSvg ORM
│   ├── service.py           # save_generated_svg()
│   └── migrations/
│       └── init.sql
├── Dockerfile               # Container definition (clones OmniSVG repo)
├── docker-compose.yml       # Local development setup
├── requirements.txt
└── README.md

🔄 Processing Flow

┌─────────────────────────────────────────────────────────────┐
│                     RunPod Job Input                         │
│   { image, model_size, num_candidates, temperature, ... }    │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│                   1. Validate Input                          │
│      Check image, model_size, num_candidates, etc.           │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│         2. Load Image (S3 or Network Volume)                 │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│              3. Generate SVG (GPU, OmniSVG)                  │
│  ImageToSVG.generate(image, num_candidates, ...)             │
│   • Preprocess image (background replace, resize → 448)      │
│   • Qwen-VL processor → token inputs                         │
│   • SketchDecoder.transformer.generate(...)                  │
│   • SVGTokenizer.process_generated_tokens(...) → SVG string  │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│       4. Save Result (Cloudflare or Network Volume)          │
│   • Cloudflare: upload .svg → CDN URL                        │
│   • Volume: write .svg file under OUTPUT_VOLUME_PATH         │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│      5. Delete Input (Optional)                              │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│      6. Save Record to Database (Optional)                   │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│      7. Send Webhook Callback (Async)                        │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│                  Return Response                             │
│   { job_id, output_url, svg, path_count, ... }               │
└─────────────────────────────────────────────────────────────┘

🚀 Quick Start

1. Build Docker Image

docker compose build
# atau
docker build -t your-username/runpod-omnisvg:latest .

2. Download model weights ke Network Volume

Layout volume default (OMNISVG_HOME=/runpod-volume/omnisvg-models):

/runpod-volume/omnisvg-models/
├── OmniSVG1.1_4B/            # berisi pytorch_model.bin
│   └── pytorch_model.bin
└── qwen-4B/                  # Qwen2.5-VL-3B-Instruct snapshot
    ├── config.json
    ├── tokenizer.json
    └── ...

Download dari HuggingFace:

pip install huggingface-hub
huggingface-cli download OmniSVG/OmniSVG1.1_4B --local-dir /runpod-volume/omnisvg-models/OmniSVG1.1_4B
huggingface-cli download Qwen/Qwen2.5-VL-3B-Instruct --local-dir /runpod-volume/omnisvg-models/qwen-4B

# Untuk model 8B:
huggingface-cli download OmniSVG/OmniSVG1.1_8B --local-dir /runpod-volume/omnisvg-models/OmniSVG1.1_8B
huggingface-cli download Qwen/Qwen2.5-VL-7B-Instruct --local-dir /runpod-volume/omnisvg-models/qwen-8B

Alternatif: kosongkan OMNISVG_MODEL_PATH / QWEN_MODEL_PATH dan worker akan auto-download dari HuggingFace pada cold-start pertama (lebih lambat).

3. Deploy ke RunPod

  1. Push image ke Docker Hub.
  2. Buat RunPod Serverless Template dengan environment variables (lihat di bawah).
  3. Mount Network Volume yang berisi model weights ke /runpod-volume.

⚙️ Environment Variables

Storage

VariableDefaultKeterangan
INPUT_STORAGE_MODEs3s3 atau volume
INPUT_VOLUME_PATH/runpod-volume/inputs/Root path untuk input volume
OUTPUT_STORAGE_MODEcloudflarecloudflare atau volume
OUTPUT_VOLUME_PATH/runpod-volume/outputs/Root path untuk SVG output di volume
DELETE_INPUT_AFTER_GENERATEfalseHapus input setelah selesai

S3 (input)

VariableKeterangan
S3_BUCKETBucket name
S3_REGIONDefault us-east-1
S3_ENDPOINT_URLOptional, untuk S3-compatible storage
AWS_ACCESS_KEY_IDCredentials
AWS_SECRET_ACCESS_KEYCredentials

Cloudflare Images (output)

VariableKeterangan
CLOUDFLARE_ACCOUNT_IDCloudflare account ID
CLOUDFLARE_API_TOKENAPI token dengan permission Images:Edit

OmniSVG

VariableDefaultKeterangan
OMNISVG_MODEL_SIZE4B3B, 4B, atau 8B
OMNISVG_HOME/runpod-volume/omnisvg-modelsRoot folder untuk model weights
OMNISVG_MODEL_PATH(auto)Path lokal / HF repo id ke OmniSVG weights folder
QWEN_MODEL_PATH(auto)Path lokal / HF repo id ke Qwen2.5-VL backbone
OMNISVG_REPO_PATH/opt/OmniSVGPath ke clone OmniSVG (sudah di-set di Dockerfile)
OMNISVG_NUM_CANDIDATES1Default jumlah kandidat per request
OMNISVG_MAX_LENGTH1024Default max token length per request
OMNISVG_TEMPERATURE0.3Default sampling temperature
OMNISVG_TOP_P0.90Default top-p
OMNISVG_TOP_K50Default top-k
OMNISVG_REP_PENALTY1.05Default repetition penalty

Database & Webhook

VariableKeterangan
ENABLE_DATABASEtrue/false
DATABASE_URLpostgresql+psycopg2://...
WEBHOOK_CALLBACK_URLURL endpoint untuk async callback
WEBHOOK_TIMEOUT_SECONDSDefault 10
WEBHOOK_AUTH_TOKENOptional bearer token untuk webhook

📥 Job Input Schema

{
  "input": {
    "image": "path/or/key/to/input.png",
    "model_size": "4B",
    "num_candidates": 1,
    "max_length": 1024,
    "temperature": 0.3,
    "top_p": 0.9,
    "top_k": 50,
    "repetition_penalty": 1.05,
    "replace_background": true,
    "return_svg": true,
    "webhook_url": "https://your-app.com/webhook/optional",
    "webhook_enabled": true
  }
}
FieldTypeDefaultKeterangan
imagestringrequiredS3 key atau path relatif di volume
model_sizestringenv default3B / 4B / 8B
num_candidatesint11-8
max_lengthint1024Maks token output
temperaturefloat0.3
top_pfloat0.9
top_kint50
repetition_penaltyfloat1.05
replace_backgroundbooltrueGanti background non-putih → putih sebelum di-feed ke model
return_svgbooltrueSertakan SVG string di response payload
webhook_urlstringenv defaultOverride webhook per-request
webhook_enabledbooltrueDisable webhook per-request

Kirim {"input": {"image": "warming-up"}} untuk warm-up worker tanpa load model.

📤 Response Schema

{
  "job_id": "abc-123",
  "status": "COMPLETED",
  "processing_time": 12.34,
  "input_storage_mode": "s3",
  "output_storage_mode": "cloudflare",
  "output_url": "https://imagedelivery.net/.../public",
  "output_volume": null,
  "format": "svg",
  "model_size": "4B",
  "num_candidates": 1,
  "path_count": 17,
  "original_size": [1024, 1024],
  "target_size": [448, 448],
  "svg_bytes": 4321,
  "svg": "<svg ...>...</svg>",
  "webhook_triggered_at": "2026-05-18T08:00:00+00:00",
  "error_message": null
}

🗄️ Database Schema

Lihat db/migrations/init.sql — tabel runpod_worker_generated_svgs.

📝 Notes

  • OmniSVG menghasilkan SVG pada canvas 448x448 (sesuai image.target_size di config.yaml). Lebar/tinggi ini di-embed di tag <svg> agar dapat di-scale via CSS / viewBox di sisi client.
  • Cloudflare Images mendukung upload SVG, tapi jika plan Anda tidak mengizinkan, set OUTPUT_STORAGE_MODE=volume dan baca file dari OUTPUT_VOLUME_PATH.
  • Cold start akan melakukan load model (~17 GB untuk 4B, ~26 GB untuk 8B). Untuk serverless RunPod, gunakan worker warm pool atau pre-warm dengan request image: "warming-up".

Contributors

rockierocker1993/runpod-worker-image-svg

0

stars

3

commits

Python

primary language

May 18, 2026

updated

README

RunPod Worker Image-to-SVG (OmniSVG)

RunPod serverless worker untuk konversi image → SVG menggunakan OmniSVG (Qwen2.5-VL backbone) dengan dukungan GPU.

Pipeline ini hasil migrasi dari worker Real-ESRGAN upscaler — semua mekanisme storage / webhook / database tetap sama, hanya stage inti upscale yang diganti menjadi image-to-svg generation.

📋 Fitur

  • Flexible Input Storage: S3 atau RunPod Network Volume
  • Flexible Output Storage: Cloudflare Images (CDN, SVG supported) atau Network Volume (.svg file)
  • ✅ Generate SVG dari gambar input menggunakan OmniSVG (3B / 4B / 8B)
  • ✅ Multi-candidate generation dengan kontrol temperature, top_p, top_k, repetition_penalty
  • ✅ Auto background replacement (white background) untuk hasil yang lebih bersih
  • ✅ Auto-delete input image setelah generate (opsional)
  • ✅ Warming-up worker tanpa memproses gambar (image: "warming-up")
  • ✅ Simpan metadata ke database PostgreSQL (opsional)
  • ✅ Webhook callback async untuk notifikasi status (success/error)
  • ✅ Models di-load dari RunPod Network Volume atau di-download dari HuggingFace
  • ✅ GPU acceleration (CUDA 12.1) dengan BF16/FP16

🖥️ System Requirements

Hardware

Model sizeGPU memoryRecommended GPU
OmniSVG-3B~17 GBRTX 3090 / A5000
OmniSVG1.1_4B~17 GBRTX 3090 / A5000
OmniSVG1.1_8B~26 GBA6000 / A100 40GB
  • RAM: 16 GB+
  • Storage: ~25 GB untuk Docker image + model weights
  • CUDA: 12.1 (di-bundle di base image)

Cloud (RunPod)

  • GPU Instance: A5000 / A6000 / A100 (sesuai model size)
  • Disk Space: 25 GB minimum
  • Network: Akses ke S3 endpoint (input) dan Cloudflare API (output)

📦 Dependencies

Python Packages

runpod
transformers==4.51.3
qwen-vl-utils==0.0.11
huggingface-hub
accelerate
einops==0.4.1
PyYAML==6.0.2
Pillow
numpy<2.0.0
CairoSVG==2.7.1
shapely==2.0.7
boto3
requests
sqlalchemy
psycopg2-binary

CUDA Libraries

  • PyTorch 2.3.0 (CUDA 12.1)
  • torchvision 0.18.0

System Libraries

  • libcairo2 / libcairo2-dev (untuk CairoSVG)
  • libgl1, libglib2.0-0 (untuk Pillow / OpenCV)
  • git (untuk clone OmniSVG repo di build time)

📁 Project Structure

runpod-worker-image-svg/
├── main.py                  # RunPod handler & pipeline flow
├── svg_generator.py         # OmniSVG image-to-SVG inference wrapper
├── db/                      # Database module
│   ├── __init__.py
│   ├── database.py
│   ├── models.py            # GeneratedSvg ORM
│   ├── service.py           # save_generated_svg()
│   └── migrations/
│       └── init.sql
├── Dockerfile               # Container definition (clones OmniSVG repo)
├── docker-compose.yml       # Local development setup
├── requirements.txt
└── README.md

🔄 Processing Flow

┌─────────────────────────────────────────────────────────────┐
│                     RunPod Job Input                         │
│   { image, model_size, num_candidates, temperature, ... }    │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│                   1. Validate Input                          │
│      Check image, model_size, num_candidates, etc.           │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│         2. Load Image (S3 or Network Volume)                 │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│              3. Generate SVG (GPU, OmniSVG)                  │
│  ImageToSVG.generate(image, num_candidates, ...)             │
│   • Preprocess image (background replace, resize → 448)      │
│   • Qwen-VL processor → token inputs                         │
│   • SketchDecoder.transformer.generate(...)                  │
│   • SVGTokenizer.process_generated_tokens(...) → SVG string  │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│       4. Save Result (Cloudflare or Network Volume)          │
│   • Cloudflare: upload .svg → CDN URL                        │
│   • Volume: write .svg file under OUTPUT_VOLUME_PATH         │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│      5. Delete Input (Optional)                              │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│      6. Save Record to Database (Optional)                   │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│      7. Send Webhook Callback (Async)                        │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│                  Return Response                             │
│   { job_id, output_url, svg, path_count, ... }               │
└─────────────────────────────────────────────────────────────┘

🚀 Quick Start

1. Build Docker Image

docker compose build
# atau
docker build -t your-username/runpod-omnisvg:latest .

2. Download model weights ke Network Volume

Layout volume default (OMNISVG_HOME=/runpod-volume/omnisvg-models):

/runpod-volume/omnisvg-models/
├── OmniSVG1.1_4B/            # berisi pytorch_model.bin
│   └── pytorch_model.bin
└── qwen-4B/                  # Qwen2.5-VL-3B-Instruct snapshot
    ├── config.json
    ├── tokenizer.json
    └── ...

Download dari HuggingFace:

pip install huggingface-hub
huggingface-cli download OmniSVG/OmniSVG1.1_4B --local-dir /runpod-volume/omnisvg-models/OmniSVG1.1_4B
huggingface-cli download Qwen/Qwen2.5-VL-3B-Instruct --local-dir /runpod-volume/omnisvg-models/qwen-4B

# Untuk model 8B:
huggingface-cli download OmniSVG/OmniSVG1.1_8B --local-dir /runpod-volume/omnisvg-models/OmniSVG1.1_8B
huggingface-cli download Qwen/Qwen2.5-VL-7B-Instruct --local-dir /runpod-volume/omnisvg-models/qwen-8B

Alternatif: kosongkan OMNISVG_MODEL_PATH / QWEN_MODEL_PATH dan worker akan auto-download dari HuggingFace pada cold-start pertama (lebih lambat).

3. Deploy ke RunPod

  1. Push image ke Docker Hub.
  2. Buat RunPod Serverless Template dengan environment variables (lihat di bawah).
  3. Mount Network Volume yang berisi model weights ke /runpod-volume.

⚙️ Environment Variables

Storage

VariableDefaultKeterangan
INPUT_STORAGE_MODEs3s3 atau volume
INPUT_VOLUME_PATH/runpod-volume/inputs/Root path untuk input volume
OUTPUT_STORAGE_MODEcloudflarecloudflare atau volume
OUTPUT_VOLUME_PATH/runpod-volume/outputs/Root path untuk SVG output di volume
DELETE_INPUT_AFTER_GENERATEfalseHapus input setelah selesai

S3 (input)

VariableKeterangan
S3_BUCKETBucket name
S3_REGIONDefault us-east-1
S3_ENDPOINT_URLOptional, untuk S3-compatible storage
AWS_ACCESS_KEY_IDCredentials
AWS_SECRET_ACCESS_KEYCredentials

Cloudflare Images (output)

VariableKeterangan
CLOUDFLARE_ACCOUNT_IDCloudflare account ID
CLOUDFLARE_API_TOKENAPI token dengan permission Images:Edit

OmniSVG

VariableDefaultKeterangan
OMNISVG_MODEL_SIZE4B3B, 4B, atau 8B
OMNISVG_HOME/runpod-volume/omnisvg-modelsRoot folder untuk model weights
OMNISVG_MODEL_PATH(auto)Path lokal / HF repo id ke OmniSVG weights folder
QWEN_MODEL_PATH(auto)Path lokal / HF repo id ke Qwen2.5-VL backbone
OMNISVG_REPO_PATH/opt/OmniSVGPath ke clone OmniSVG (sudah di-set di Dockerfile)
OMNISVG_NUM_CANDIDATES1Default jumlah kandidat per request
OMNISVG_MAX_LENGTH1024Default max token length per request
OMNISVG_TEMPERATURE0.3Default sampling temperature
OMNISVG_TOP_P0.90Default top-p
OMNISVG_TOP_K50Default top-k
OMNISVG_REP_PENALTY1.05Default repetition penalty

Database & Webhook

VariableKeterangan
ENABLE_DATABASEtrue/false
DATABASE_URLpostgresql+psycopg2://...
WEBHOOK_CALLBACK_URLURL endpoint untuk async callback
WEBHOOK_TIMEOUT_SECONDSDefault 10
WEBHOOK_AUTH_TOKENOptional bearer token untuk webhook

📥 Job Input Schema

{
  "input": {
    "image": "path/or/key/to/input.png",
    "model_size": "4B",
    "num_candidates": 1,
    "max_length": 1024,
    "temperature": 0.3,
    "top_p": 0.9,
    "top_k": 50,
    "repetition_penalty": 1.05,
    "replace_background": true,
    "return_svg": true,
    "webhook_url": "https://your-app.com/webhook/optional",
    "webhook_enabled": true
  }
}
FieldTypeDefaultKeterangan
imagestringrequiredS3 key atau path relatif di volume
model_sizestringenv default3B / 4B / 8B
num_candidatesint11-8
max_lengthint1024Maks token output
temperaturefloat0.3
top_pfloat0.9
top_kint50
repetition_penaltyfloat1.05
replace_backgroundbooltrueGanti background non-putih → putih sebelum di-feed ke model
return_svgbooltrueSertakan SVG string di response payload
webhook_urlstringenv defaultOverride webhook per-request
webhook_enabledbooltrueDisable webhook per-request

Kirim {"input": {"image": "warming-up"}} untuk warm-up worker tanpa load model.

📤 Response Schema

{
  "job_id": "abc-123",
  "status": "COMPLETED",
  "processing_time": 12.34,
  "input_storage_mode": "s3",
  "output_storage_mode": "cloudflare",
  "output_url": "https://imagedelivery.net/.../public",
  "output_volume": null,
  "format": "svg",
  "model_size": "4B",
  "num_candidates": 1,
  "path_count": 17,
  "original_size": [1024, 1024],
  "target_size": [448, 448],
  "svg_bytes": 4321,
  "svg": "<svg ...>...</svg>",
  "webhook_triggered_at": "2026-05-18T08:00:00+00:00",
  "error_message": null
}

🗄️ Database Schema

Lihat db/migrations/init.sql — tabel runpod_worker_generated_svgs.

📝 Notes

  • OmniSVG menghasilkan SVG pada canvas 448x448 (sesuai image.target_size di config.yaml). Lebar/tinggi ini di-embed di tag <svg> agar dapat di-scale via CSS / viewBox di sisi client.
  • Cloudflare Images mendukung upload SVG, tapi jika plan Anda tidak mengizinkan, set OUTPUT_STORAGE_MODE=volume dan baca file dari OUTPUT_VOLUME_PATH.
  • Cold start akan melakukan load model (~17 GB untuk 4B, ~26 GB untuk 8B). Untuk serverless RunPod, gunakan worker warm pool atau pre-warm dengan request image: "warming-up".

Contributors

Languages

Python

98.0%

kvlang

1.6%