Spaces:
Sleeping
Sleeping
Download Dockerfile from junyoung137/image-tool: direct link, hf CLI and curl.
- Browser
- Download file 11.9 kB
-
https://huggingface.co/spaces/junyoung137/image-tool/resolve/main/Dockerfile
- Command line
-
hf download hf://spaces/junyoung137/image-tool/Dockerfile
-
curl -L -o Dockerfile https://huggingface.co/spaces/junyoung137/image-tool/resolve/main/Dockerfile
11.9 kB
| ############################################################################### | |
| # Dockerfile β HF Spaces (Docker SDK, CPU-only, port 7860) | |
| # | |
| # Two-stage build: | |
| # Stage A β node:22-slim β compile React/Vite frontend (VITE_API_BASE="") | |
| # Stage B β python:3.11-slim β runtime with all ML models baked in | |
| # | |
| # HF Spaces rules enforced: | |
| # β’ Non-root user uid=1000 (useradd -m -u 1000 user) | |
| # β’ All writable runtime dirs under /tmp (STORAGE_DIR, JOBS_DB_DIR) | |
| # β’ Model cache baked at build time under /home/user (readable by uid 1000) | |
| # β’ Port 7860 | |
| # | |
| # Layer-size note | |
| # --------------- | |
| # Model downloads and COPY instructions run AFTER "USER user" so files are | |
| # created owned by uid 1000 directly. The only root-owned step after mkdir is | |
| # facexlib (must write to site-packages). This eliminates the old | |
| # "RUN chown -R 1000:1000 /home/user" layer that duplicated ~1.5 GB of model | |
| # weights via Docker's copy-on-write mechanism. | |
| ############################################################################### | |
| ############################################################################### | |
| # Stage A β Build the React / Vite frontend | |
| ############################################################################### | |
| FROM node:22-slim AS frontend-build | |
| WORKDIR /build/frontend | |
| # Install npm deps (cached until package*.json changes) | |
| COPY frontend/package.json frontend/package-lock.json ./ | |
| RUN npm ci | |
| # Build with VITE_API_BASE="" so every fetch() in the compiled JS uses a | |
| # relative path (same-origin), not hardcoded http://localhost:8000. | |
| COPY frontend/ . | |
| RUN VITE_API_BASE="" npm run build | |
| # Output lives at /build/frontend/dist | |
| ############################################################################### | |
| # Stage B β Python 3.11 runtime | |
| ############################################################################### | |
| FROM python:3.11-slim | |
| # ββ OS dependencies βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # libgl1 + libglib2.0-0 : opencv-python requirement | |
| # libsm6 libxext6 libxrender1 : X11 stubs required by cv2 on headless Linux | |
| # wget ca-certificates : download model weights in subsequent RUN steps | |
| RUN apt-get update \ | |
| && apt-get install -y --no-install-recommends \ | |
| libgl1 \ | |
| libglib2.0-0 \ | |
| libsm6 \ | |
| libxext6 \ | |
| libxrender1 \ | |
| wget \ | |
| ca-certificates \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # ββ Non-root user (HF Spaces hard requirement) ββββββββββββββββββββββββββββββββ | |
| RUN useradd -m -u 1000 user | |
| # ββ Python packages βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Step 1: CPU-only torch + torchvision from the dedicated wheel index. | |
| # Versions pinned to match the local dev environment (torch 2.8.0, tv 0.23.0). | |
| # Installing from pytorch.org/whl/cpu guarantees no CUDA binary is pulled. | |
| # Step 2: rest of requirements.txt. pip finds torch>=2.2.0 already satisfied | |
| # by the CPU wheel above and skips it; all other deps resolve normally. | |
| COPY requirements.txt /tmp/requirements.txt | |
| RUN pip install --no-cache-dir \ | |
| "torch==2.8.0" "torchvision==0.23.0" \ | |
| --index-url https://download.pytorch.org/whl/cpu \ | |
| && pip install --no-cache-dir -r /tmp/requirements.txt | |
| # ββ facexlib weights (must run as root β writes to site-packages) βββββββββββββ | |
| # URLs verified against facexlib 0.3.0 source: | |
| # recognition/__init__.py : recognition_arcface_ir_se50.pth β v0.1.0 | |
| # __init__.py : detection_Resnet50_Final.pth β v0.1.0 | |
| # __init__.py : parsing_parsenet.pth β v0.2.2 | |
| # | |
| # FaceRestoreHelper.__init__ triggers the download of detection + parsing models | |
| # automatically via load_file_from_url. ArcFace is called separately because | |
| # facerestore_engine.py loads it independently (raises FileNotFoundError if absent). | |
| RUN python -c "\ | |
| import torch; \ | |
| from facexlib.utils.face_restoration_helper import FaceRestoreHelper; \ | |
| from facexlib.utils.misc import load_file_from_url; \ | |
| import facexlib; \ | |
| from pathlib import Path; \ | |
| weights_dir = str(Path(facexlib.__file__).parent / 'weights'); \ | |
| print('facexlib weights dir:', weights_dir); \ | |
| print('Pre-downloading RetinaFace (detection_Resnet50_Final.pth, 104 MB) and ParseNet (parsing_parsenet.pth, 81 MB)...'); \ | |
| FaceRestoreHelper(upscale_factor=1, face_size=512, crop_ratio=(1, 1), det_model='retinaface_resnet50', save_ext='png', use_parse=True, device=torch.device('cpu')); \ | |
| print('RetinaFace + ParseNet ready.'); \ | |
| print('Downloading ArcFace IR-SE50 (recognition_arcface_ir_se50.pth, 167 MB)...'); \ | |
| load_file_from_url(url='https://github.com/xinntao/facexlib/releases/download/v0.1.0/recognition_arcface_ir_se50.pth', model_dir='facexlib/weights', progress=True, save_dir=weights_dir); \ | |
| print('All facexlib weights downloaded.')" | |
| # ββ Pre-create model/cache dirs and chown while they are EMPTY (cheap) ββββββββ | |
| # chown on empty directories has zero copy-on-write cost. | |
| # All subsequent writes (model downloads, COPY) happen as uid 1000 directly. | |
| RUN mkdir -p \ | |
| /home/user/app/ml_models/birefnet \ | |
| /home/user/app/backend/ml_models/codeformer \ | |
| /home/user/.cache/huggingface \ | |
| /home/user/.paddleocr \ | |
| && chown -R 1000:1000 \ | |
| /home/user/app \ | |
| /home/user/.cache \ | |
| /home/user/.paddleocr | |
| # ββ Switch to uid 1000 β all subsequent RUN steps write as the target user ββββ | |
| USER user | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # MODEL DOWNLOADS (all happen at build time β zero network I/O at startup) | |
| # Running as uid 1000 so files are created with correct ownership directly β | |
| # no chown layer needed at the end. | |
| # Layer order: stable downloads first so code changes don't invalidate them. | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # ββ RealESRGAN_x4plus.pth (64 MB) βββββββββββββββββββββββββββββββββββββββββββ | |
| # Official release: https://github.com/xinntao/Real-ESRGAN/releases/tag/v0.1.0 | |
| # Destination matches UPSCALE_MODEL_PATH = BASE_DIR / "ml_models" / "RealESRGAN_x4plus.pth" | |
| RUN wget --tries=3 --no-verbose --show-progress \ | |
| -O /home/user/app/ml_models/RealESRGAN_x4plus.pth \ | |
| "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth" | |
| # ββ codeformer.pth (360 MB) βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Official release: https://github.com/sczhou/CodeFormer/releases/tag/v0.1.0 | |
| # Destination matches CODEFORMER_MODEL_PATH = backend/ml_models/codeformer/codeformer.pth | |
| RUN wget --tries=3 --no-verbose --show-progress \ | |
| -O /home/user/app/backend/ml_models/codeformer/codeformer.pth \ | |
| "https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth" | |
| # ββ BiRefNet (~424 MB, HuggingFace Hub) βββββββββββββββββββββββββββββββββββββ | |
| # Uses snapshot_download (no model instantiation) so it is reliable in a | |
| # headless build environment. The cache_dir matches BIREFNET_MODEL_CACHE in | |
| # config.py (BASE_DIR / "ml_models" / "birefnet"). trust_remote_code=True is | |
| # NOT needed here β snapshot_download just copies files. At runtime, | |
| # from_pretrained(..., trust_remote_code=True) reads the same cache. | |
| RUN python -c "\ | |
| from huggingface_hub import snapshot_download; \ | |
| print('Downloading BiRefNet from HuggingFace Hub (~424 MB)...'); \ | |
| snapshot_download(repo_id='ZhengPeng7/BiRefNet', cache_dir='/home/user/app/ml_models/birefnet'); \ | |
| print('BiRefNet download complete.')" | |
| # ββ PaddleOCR models (~200 MB, text detection/recognition/orientation) βββββββ | |
| # HOME is set to /home/user so models land in /home/user/.paddleocr, which is | |
| # exactly where the runtime process will look for them. | |
| # Same PaddleOCR constructor args as ocr_service.py: use_textline_orientation, | |
| # lang="en", enable_mkldnn=False (safe on Linux CPU, avoids OneDNN issues). | |
| RUN HOME=/home/user python -c "\ | |
| from paddleocr import PaddleOCR; \ | |
| print('Pre-warming PaddleOCR (first-run model download ~200 MB)...'); \ | |
| PaddleOCR(use_textline_orientation=True, lang='en', enable_mkldnn=False); \ | |
| print('PaddleOCR pre-warm complete.')" | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # SOURCE & COMMITTED WEIGHTS (change more often than model downloads β last) | |
| # --chown=user:user ensures uid 1000 owns all copied files without a separate | |
| # chown layer (avoids copy-on-write duplication of large files). | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # ββ Backend source (.dockerignore excludes codeformer.pth β already downloaded) | |
| COPY --chown=user:user backend/ /home/user/app/backend/ | |
| # ββ lama.pth (197 MB) βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # No reliable public download URL exists for the big-lama TorchScript export. | |
| # This file MUST be committed to the repository via Git LFS. | |
| # Register with: git lfs track "ml_models/lama.pth" | |
| # Destination matches MODEL_PATH = BASE_DIR / "ml_models" / "lama.pth" | |
| COPY --chown=user:user ml_models/lama.pth /home/user/app/ml_models/lama.pth | |
| # ββ Built React app from Stage A βββββββββββββββββββββββββββββββββββββββββββββ | |
| # Destination matches the path computed in main.py: | |
| # Path(__file__).resolve().parent.parent.parent / "frontend" / "dist" | |
| # = /home/user/app/frontend/dist (when __file__ is backend/app/main.py) | |
| COPY --chown=user:user --from=frontend-build /build/frontend/dist /home/user/app/frontend/dist/ | |
| # ββ Runtime environment variables βββββββββββββββββββββββββββββββββββββββββββββ | |
| # Every variable here has a local-safe default in the Python code; these | |
| # overrides are ONLY active inside the container (start.bat sets none of them). | |
| ENV HOME=/home/user \ | |
| PORT=7860 \ | |
| SERVE_FRONTEND=true \ | |
| PUBLIC_BASE_URL="" \ | |
| ALLOWED_ORIGINS="*" \ | |
| STORAGE_DIR=/tmp/storage \ | |
| JOBS_DB_DIR=/tmp/data \ | |
| HF_HOME=/home/user/.cache/huggingface \ | |
| HF_HUB_DISABLE_PROGRESS_BARS=1 | |
| EXPOSE 7860 | |
| # Working directory mirrors start.bat: `cd backend && uvicorn app.main:app` | |
| # "app.main:app" resolves to /home/user/app/backend/app/main.py | |
| WORKDIR /home/user/app/backend | |
| # No --reload (dev-only). --host 0.0.0.0 required for HF Spaces to route traffic. | |
| CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"] | |