A REST API for automatic document layout detection. Receives JPG/PNG page images and returns a list of detected regions (text blocks, titles, tables, figures, etc.) as JSON.
Built on surya (vikp/surya_layout3) and runs GPU-accelerated inside Docker.
POST /detect— upload an image, get back structured layout regions as JSONGET /health— service health and configuration check- GPU acceleration via NVIDIA CUDA 12.1
- Model weights downloaded automatically on first start from HuggingFace
- Persistent model cache via Docker volume (no re-download on restart)
| Label | Description |
|---|---|
Text |
Body text block |
Title |
Main title |
Section-header |
Section heading |
Table |
Table |
Picture |
Figure / image |
Caption |
Figure caption |
Page-header |
Page header |
Page-footer |
Page footer |
Footnote |
Footnote |
Formula |
Mathematical formula |
List-item |
List entry |
- Docker Desktop with NVIDIA Container Toolkit enabled
- NVIDIA GPU (tested: RTX 4070 SUPER, 12 GB VRAM)
- NVIDIA driver ≥ 525
Install NVIDIA Container Toolkit (once, Linux):
distribution=$(. /etc/os-release; echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list \
| sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart dockerOn Windows: enable GPU in Docker Desktop → Settings → Resources → GPU.
docker compose up --buildOn first start, ~500 MB of model weights are downloaded from HuggingFace (one-time only). The service is then available on port 5060.
curl http://localhost:5060/health{"status": "ok", "backend": "surya", "threshold": 0.5}curl -X POST \
-F "file=@page.jpg" \
http://localhost:5060/detectResponse:
[
{
"type": "Title",
"score": 0.97,
"bbox": {"x1": 72.0, "y1": 84.0, "x2": 523.0, "y2": 107.0},
"page_width": 1240,
"page_height": 1754
},
{
"type": "Text",
"score": 0.95,
"bbox": {"x1": 72.0, "y1": 130.0, "x2": 523.0, "y2": 420.0},
"page_width": 1240,
"page_height": 1754
}
]import requests
with open("page.jpg", "rb") as f:
response = requests.post(
"http://localhost:5060/detect",
files={"file": ("page.jpg", f, "image/jpeg")},
)
for region in response.json():
print(f"{region['type']:20s} score={region['score']:.2f} bbox={region['bbox']}")Set via environment variables or in docker-compose.yml:
| Variable | Default | Description |
|---|---|---|
SCORE_THRESHOLD |
0.5 |
Minimum confidence score (0–1). Regions below this are excluded. |
HF_HOME |
/models |
HuggingFace model cache path inside the container |
Example — stricter threshold:
SCORE_THRESHOLD=0.7 docker compose uplayout-api/
├── Dockerfile # PyTorch 2.3.1 + CUDA 12.1 base image
├── docker-compose.yml # GPU access, port 5060, model cache volume
├── requirements.txt # fastapi, uvicorn, surya-ocr, Pillow
└── app/
└── main.py # FastAPI app — POST /detect, GET /health
Remove the deploy block from docker-compose.yml:
# delete these lines:
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: 1
# capabilities: [gpu]And swap the base image in Dockerfile:
FROM python:3.11-slimInference time increases to roughly 5–30 seconds per page.
| Model | vikp/surya_layout3 (HuggingFace) |
| Framework | surya-ocr 0.6.3 + PyTorch 2.3.1 |
| CUDA | 12.1 + cuDNN 8 |
| API | FastAPI + uvicorn |
| Port | 5060 |
| Inference (GPU) | ~100–300 ms per page |