{"id":51542632,"url":"https://github.com/psylsph/opencv-image-edit","last_synced_at":"2026-07-09T15:01:30.062Z","repository":{"id":370183988,"uuid":"1293492841","full_name":"psylsph/opencv-image-edit","owner":"psylsph","description":"Mobile-first AI image editor powered by OpenCV 5 — background blur/remove, film grain, AI upscale, color filters. Vanilla HTML/CSS/JS PWA + FastAPI. ~1.3GB Docker image, no torch.","archived":false,"fork":false,"pushed_at":"2026-07-08T13:11:25.000Z","size":321,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-07-08T15:07:01.423Z","etag":null,"topics":["ai","computer-vision","docker","fastapi","image-processing","opencv","pwa","python"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/psylsph.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-08T11:12:00.000Z","updated_at":"2026-07-08T13:11:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/psylsph/opencv-image-edit","commit_stats":null,"previous_names":["psylsph/opencv-image-edit"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/psylsph/opencv-image-edit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psylsph%2Fopencv-image-edit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psylsph%2Fopencv-image-edit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psylsph%2Fopencv-image-edit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psylsph%2Fopencv-image-edit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/psylsph","download_url":"https://codeload.github.com/psylsph/opencv-image-edit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psylsph%2Fopencv-image-edit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35303292,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-09T02:00:07.329Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","computer-vision","docker","fastapi","image-processing","opencv","pwa","python"],"created_at":"2026-07-09T15:01:29.433Z","updated_at":"2026-07-09T15:01:30.051Z","avatar_url":"https://github.com/psylsph.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OpenCV Image Editor\n\nMobile-first AI image editor — background blur/removal, film grain, 4× AI upscale, color filters. One Docker container, no cloud, runs on a CPU.\n\n![build status](https://img.shields.io/badge/build-passing-brightgreen) ![python](https://img.shields.io/badge/python-3.12-blue) ![license](https://img.shields.io/badge/license-MIT-green) ![image size](https://img.shields.io/badge/docker_image-~1.3GB-blue)\n\n---\n\n## 📸 Screenshots\n\n| Desktop | Mobile |\n| --- | --- |\n| ![Desktop view](docs/screenshots/desktop.png) | ![Mobile view](docs/screenshots/mobile.png) |\n\n---\n\n## ✨ Features\n\n### 🎨 Editing\n- **Background** — blur or remove with U2NetP matting (ONNX). Adjustable blur strength (1–50).\n- **Film grain** — luminance-aware, Rec.601-weighted noise that preserves subject detail.\n- **AI upscale** — 2× / 4× via OpenCV DNN `cv2.dnn_superres.EDSR` with LANCZOS4 fallback.\n- **Filters** — brightness, contrast, saturation, sharpness, sepia, grayscale, blur, vignette, unsharp mask, auto-enhance.\n- **Before/after comparison** — side-by-side and diff overlay.\n- **Presets** — one-tap *Portrait*, *Landscape*, *Vintage*, *Minimal*.\n\n### 🌐 Web UI\n- Mobile-first PWA — installable on iOS/Android, works offline after first load.\n- Dark theme, no build step, vanilla JS — total client payload \u003c 30 KB.\n- Server-side rendering of the comparison image so the download is one tap.\n\n### 🛡️ Production\n- Per-IP rate limiting (`slowapi`).\n- Prometheus metrics on a separate port (`:9090`).\n- Health endpoint, structured errors, request size accounting.\n- Image size and dimension limits enforced server-side.\n- Multi-stage Docker build — model files baked in at build time, no runtime download.\n\n---\n\n## 🚀 Getting Started\n\nYou need **Docker** (20.10+) and **~2 GB of free disk** for the image. That's it.\n\n```bash\ngit clone \u003crepo-url\u003e\ncd opencv-image-edit\ndocker compose up -d\n```\n\nOpen **http://localhost:8000** in your browser. The first request after boot takes ~3 s while the AI models warm up; subsequent requests are sub-second.\n\n\u003e **Updating:** `docker compose pull \u0026\u0026 docker compose up -d`\n\u003e **Stopping:** `docker compose down` (your settings and temp files live in Docker volumes and survive restarts).\n\n---\n\n## 📖 Usage\n\n1. **Pick an image.** Tap the upload area, choose a file, or use the camera. PNG, JPEG, WebP, and HEIC/HEIF are all accepted (max 10 MB, max 1536 px on the long edge).\n2. **Pick a preset** — or skip and dial in the controls manually.\n3. **Tap Process.** The result appears below in ~1 s. Use the before/after slider to compare.\n4. **Download** the result as PNG. If background removal was on, the PNG is RGBA so the alpha channel survives.\n\n### Presets\n\n- **👤 Portrait** — soft background blur + light grain + 2× upscale + slight color punch.\n- **🏞️ Landscape** — vivid colors + auto-enhance + 2× upscale (no grain).\n- **🎬 Vintage** — strong grain + sepia + vignette, no upscale.\n- **✨ Minimal** — background removal only, transparent PNG output.\n\nAll settings in a preset are exposed as sliders and toggles in the UI — you can tweak them after picking the preset.\n\n---\n\n## 🔧 Configuration\n\nAll settings are environment variables. Override them in `docker-compose.yml` or a `.env` file.\n\n| Variable | Default | Description |\n| --- | --- | --- |\n| `HOST` | `0.0.0.0` | Bind address. |\n| `PORT` | `8000` | HTTP port (UI + API). |\n| `DEBUG` | `false` | Enable FastAPI debug mode. |\n| `LOG_LEVEL` | `INFO` | One of `DEBUG`/`INFO`/`WARNING`/`ERROR`/`CRITICAL`. |\n| `MAX_IMAGE_SIZE_MB` | `10` | Reject uploads larger than this. |\n| `MAX_IMAGE_DIMENSION` | `1536` | Downscale uploads whose long edge exceeds this. |\n| `MODEL_DIR` | `./models` | Where U2NetP / EDSR weights live. |\n| `RATE_LIMIT_REQUESTS` | `10` | Per-IP request budget. |\n| `RATE_LIMIT_PERIOD` | `60` | Window for the rate limit, in seconds. |\n| `ENABLE_METRICS` | `true` | Expose Prometheus metrics. |\n| `METRICS_PORT` | `9090` | Port for the metrics endpoint. |\n\nCopy `.env.example` to `.env` and edit — Docker Compose picks it up automatically.\n\n---\n\n## 🏗️ Architecture\n\nThe whole app is one FastAPI process plus a static PWA. The pipeline is a single OpenCV 5 graph — every stage reads BGR frames in and writes BGR frames out, so there is no round-trip through PIL, no `torch`, and no `rembg`. The only AI model that touches the image is the U2NetP matting network (ONNX Runtime), and the AI upscaler is `cv2.dnn_superres.EDSR` (shipped inside `opencv-contrib-python-headless`).\n\nThe pipeline orchestrator (`app/pipeline/__init__.py`) chains **preprocess → background → grain → upscale → filters → compare** as a single function call. Each stage can be enabled/disabled independently; the orchestrator is the only place that knows about ordering. This is what makes presets possible — a preset is just a `ProcessRequest` with some fields set.\n\nThe HTTP layer (`app/api/`) is intentionally thin: `health`, `presets`, and `process`. The static PWA lives in `web/` and is mounted at `/`. The whole thing is small enough that a single `python -m app.main` brings it up; Docker just standardises the environment and bakes in the model weights.\n\n---\n\n## 🛠️ Development\n\nYou need **Python 3.12+**.\n\n```bash\n# 1. Get the code\ngit clone \u003crepo-url\u003e\ncd opencv-image-edit\n\n# 2. Virtual env\npython3.12 -m venv .venv\nsource .venv/bin/activate\n\n# 3. Dependencies\npip install -r requirements.txt\n\n# 4. Download model weights (U2NetP + EDSR x2/x4)\npython scripts/download_models.py ./models\n\n# 5. Run\npython -m app.main\n# → http://localhost:8000\n```\n\nThe dev loop is `uvicorn app.main:app --reload` if you want hot reload; the included `python -m app.main` is the production entrypoint and reads the same env vars.\n\n### Project layout\n\n```\nopencv-image-edit/\n├── app/\n│   ├── api/          # FastAPI routers (health, presets, process)\n│   ├── pipeline/     # preprocess, background, grain, upscale, filters, compare\n│   ├── config.py     # pydantic-settings\n│   └── main.py       # FastAPI app + lifespan\n├── web/              # Static PWA (HTML/CSS/JS/manifest/SW/icons)\n├── scripts/          # download_models.py + tests\n├── tests/            # pytest suite\n├── docs/             # screenshots, design notes\n├── Dockerfile        # multi-stage, ~1.3 GB final image\n├── docker-compose.yml\n└── requirements.txt\n```\n\n---\n\n## 🧪 Testing\n\n```bash\nsource .venv/bin/activate\npytest tests/ -q\n```\n\nThe suite is ~80% coverage on the pipeline; the AI models are exercised in tests that explicitly require the matting model (and skip if it's not on disk). Use `pytest -m \"not slow\"` to skip the AI-dependent tests.\n\n---\n\n## 🐛 Troubleshooting\n\n**`Bind for 0.0.0.0:8000 failed: port is already allocated`**\nSomething else is using port 8000. Either stop it, or remap in `docker-compose.yml`:\n```yaml\nports:\n  - \"8765:8000\"\n  - \"9765:9090\"\n```\n\n**First request takes 5–10 s**\nThat's the AI models warming up (U2NetP + EDSR). Subsequent requests are sub-second. The warmup is also why the Docker healthcheck has a 30 s `start_period`.\n\n**HEIC files from iPhone aren't loading**\nMake sure `pillow-heif` is installed — it is in `requirements.txt`. If you built a custom image without it, rebuild with `docker compose build --no-cache`.\n\n**`429 Too Many Requests` immediately**\nDefault is 10 requests per 60 s per IP. Raise `RATE_LIMIT_REQUESTS` and `RATE_LIMIT_PERIOD` in your `.env`, then `docker compose up -d`.\n\n**`Image too large` on a 5 MB JPEG**\nAfter the on-disk size check, we also downscale anything whose long edge exceeds `MAX_IMAGE_DIMENSION` (default 1536 px). Lower the dimension in `.env` for very large images.\n\n**Models missing inside the container**\nThe Dockerfile downloads them at build time. If you started the container before the download finished, rebuild: `docker compose build --no-cache`.\n\n**`pillow_heif.register_heif_opener()` error on startup**\nYou installed `opencv-image-edit` somewhere that doesn't have `pillow-heif` in the same venv. `pip install -r requirements.txt` in the same interpreter.\n\n---\n\n## 📜 License\n\nMIT — see `LICENSE` (or just use it; the intent is permissive).\n\n---\n\n## 🙏 Credits\n\n- **U2NetP** matting model — [danielgatis/rembg](https://github.com/danielgatis/rembg) (Apache 2.0).\n- **EDSR** super-resolution model — [Saafke/EDSR_Tensorflow](https://github.com/Saafke/EDSR_Tensorflow) (MIT).\n- **OpenCV 5** — [opencv.org](https://opencv.org) (Apache 2.0).\n- **FastAPI** — [fastapi.tiangolo.com](https://fastapi.tiangolo.com) (MIT).\n- **ONNX Runtime** — [onnxruntime.ai](https://onnxruntime.ai) (MIT).\n- **Prometheus** client — [prometheus.io](https://prometheus.io) (Apache 2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpsylsph%2Fopencv-image-edit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpsylsph%2Fopencv-image-edit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpsylsph%2Fopencv-image-edit/lists"}