Spaces:
Sleeping
Sleeping
perf: aggressive build optimizations - remove albumentations, use plain uvicorn, multi-stage docker build
Browse files- Dockerfile +30 -11
- inference.py +23 -9
- requirements.txt +1 -2
Dockerfile
CHANGED
|
@@ -1,28 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
FROM python:3.10-slim
|
| 2 |
|
| 3 |
-
# Install system deps
|
| 4 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 5 |
libglib2.0-0 \
|
| 6 |
libsm6 \
|
| 7 |
libxext6 \
|
| 8 |
-
|
|
|
|
| 9 |
libgl1 \
|
| 10 |
-
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
| 11 |
|
| 12 |
WORKDIR /app
|
| 13 |
|
| 14 |
-
#
|
| 15 |
-
COPY
|
| 16 |
-
RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \
|
| 17 |
-
pip install --no-cache-dir --default-timeout=1000 -r requirements.txt
|
| 18 |
|
| 19 |
-
#
|
| 20 |
COPY trainedmodels/ ./trainedmodels/
|
| 21 |
|
| 22 |
-
#
|
| 23 |
COPY app.py inference.py ./
|
| 24 |
|
| 25 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
EXPOSE 7860
|
| 27 |
|
| 28 |
-
|
|
|
|
|
|
| 1 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 2 |
+
# Multi-stage build: Smaller final image, faster rebuilds
|
| 3 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 4 |
+
|
| 5 |
+
# βββ Stage 1: Build dependencies ββββββββββββββββββββββββββββββββββββββββββ
|
| 6 |
+
FROM python:3.10-slim as builder
|
| 7 |
+
|
| 8 |
+
WORKDIR /build
|
| 9 |
+
|
| 10 |
+
# Copy and install Python packages
|
| 11 |
+
COPY requirements.txt .
|
| 12 |
+
RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \
|
| 13 |
+
pip install --no-cache-dir --default-timeout=1000 --prefix=/install -r requirements.txt
|
| 14 |
+
|
| 15 |
+
# βββ Stage 2: Runtime (minimal) βββββββββββββββββββββββββββββββββββββββββββ
|
| 16 |
FROM python:3.10-slim
|
| 17 |
|
| 18 |
+
# Install only runtime system deps (minimal OpenCV requirements)
|
| 19 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 20 |
libglib2.0-0 \
|
| 21 |
libsm6 \
|
| 22 |
libxext6 \
|
| 23 |
+
libxrender1 \
|
| 24 |
+
libgomp1 \
|
| 25 |
libgl1 \
|
| 26 |
+
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/cache/apt/*
|
| 27 |
|
| 28 |
WORKDIR /app
|
| 29 |
|
| 30 |
+
# Copy installed packages from builder stage
|
| 31 |
+
COPY --from=builder /install /usr/local
|
|
|
|
|
|
|
| 32 |
|
| 33 |
+
# Copy model files (cached unless model changes)
|
| 34 |
COPY trainedmodels/ ./trainedmodels/
|
| 35 |
|
| 36 |
+
# Copy application code (changes most frequently)
|
| 37 |
COPY app.py inference.py ./
|
| 38 |
|
| 39 |
+
# Environment optimizations
|
| 40 |
+
ENV PYTHONUNBUFFERED=1 \
|
| 41 |
+
PYTHONDONTWRITEBYTECODE=1 \
|
| 42 |
+
PIP_NO_CACHE_DIR=1
|
| 43 |
+
|
| 44 |
EXPOSE 7860
|
| 45 |
|
| 46 |
+
# Use exec form for better signal handling
|
| 47 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"]
|
inference.py
CHANGED
|
@@ -5,7 +5,6 @@ import torch
|
|
| 5 |
import torch.nn as nn
|
| 6 |
import torch.nn.functional as F
|
| 7 |
from torchvision import models
|
| 8 |
-
import albumentations as A
|
| 9 |
from pathlib import Path
|
| 10 |
|
| 11 |
# βββ Architecture (must match training notebook exactly) βββββββββββββββββββββββ
|
|
@@ -165,12 +164,27 @@ def apply_jet_colormap(gray_img):
|
|
| 165 |
return (np.stack([r, g, b], axis=-1) * 255).astype(np.uint8)
|
| 166 |
|
| 167 |
|
| 168 |
-
def
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
|
| 175 |
|
| 176 |
def preprocess_image(image_path: str, meta: dict) -> torch.Tensor:
|
|
@@ -179,8 +193,8 @@ def preprocess_image(image_path: str, meta: dict) -> torch.Tensor:
|
|
| 179 |
if image is None:
|
| 180 |
raise ValueError(f"Could not read image: {image_path}")
|
| 181 |
|
| 182 |
-
|
| 183 |
-
image =
|
| 184 |
|
| 185 |
image = image.astype(np.float32) / 255.0
|
| 186 |
image = (image - meta["global_mean"]) / meta["global_std"]
|
|
|
|
| 5 |
import torch.nn as nn
|
| 6 |
import torch.nn.functional as F
|
| 7 |
from torchvision import models
|
|
|
|
| 8 |
from pathlib import Path
|
| 9 |
|
| 10 |
# βββ Architecture (must match training notebook exactly) βββββββββββββββββββββββ
|
|
|
|
| 164 |
return (np.stack([r, g, b], axis=-1) * 255).astype(np.uint8)
|
| 165 |
|
| 166 |
|
| 167 |
+
def apply_clahe_cv2(image, clip_limit=2.0, tile_grid_size=(8, 8)):
|
| 168 |
+
"""Apply CLAHE using OpenCV (replaces albumentations)."""
|
| 169 |
+
clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)
|
| 170 |
+
return clahe.apply(image)
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
def preprocess_with_cv2(image, image_size=512):
|
| 174 |
+
"""Preprocessing pipeline using pure OpenCV (replaces albumentations)."""
|
| 175 |
+
# CLAHE
|
| 176 |
+
image = apply_clahe_cv2(image, clip_limit=2.0, tile_grid_size=(8, 8))
|
| 177 |
+
|
| 178 |
+
# Center crop 350x350
|
| 179 |
+
h, w = image.shape[:2]
|
| 180 |
+
start_y = (h - 350) // 2
|
| 181 |
+
start_x = (w - 350) // 2
|
| 182 |
+
image = image[start_y:start_y+350, start_x:start_x+350]
|
| 183 |
+
|
| 184 |
+
# Resize to target size
|
| 185 |
+
image = cv2.resize(image, (image_size, image_size), interpolation=cv2.INTER_LINEAR)
|
| 186 |
+
|
| 187 |
+
return image
|
| 188 |
|
| 189 |
|
| 190 |
def preprocess_image(image_path: str, meta: dict) -> torch.Tensor:
|
|
|
|
| 193 |
if image is None:
|
| 194 |
raise ValueError(f"Could not read image: {image_path}")
|
| 195 |
|
| 196 |
+
# Use OpenCV preprocessing instead of albumentations
|
| 197 |
+
image = preprocess_with_cv2(image, image_size=meta["image_size"])
|
| 198 |
|
| 199 |
image = image.astype(np.float32) / 255.0
|
| 200 |
image = (image - meta["global_mean"]) / meta["global_std"]
|
requirements.txt
CHANGED
|
@@ -4,9 +4,8 @@ torch==2.0.1+cpu
|
|
| 4 |
torchvision==0.15.2+cpu
|
| 5 |
|
| 6 |
fastapi==0.111.0
|
| 7 |
-
uvicorn
|
| 8 |
python-multipart==0.0.9
|
| 9 |
opencv-python-headless==4.9.0.80
|
| 10 |
-
albumentations==1.4.2
|
| 11 |
numpy==1.26.4
|
| 12 |
Pillow==10.3.0
|
|
|
|
| 4 |
torchvision==0.15.2+cpu
|
| 5 |
|
| 6 |
fastapi==0.111.0
|
| 7 |
+
uvicorn==0.29.0
|
| 8 |
python-multipart==0.0.9
|
| 9 |
opencv-python-headless==4.9.0.80
|
|
|
|
| 10 |
numpy==1.26.4
|
| 11 |
Pillow==10.3.0
|