Skip to content

KatherLab/wsi-viewer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

22 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

WSI Browser

A modern, high-performance whole-slide image (WSI) viewer for digital pathology, built with FastAPI, Vue.js, and OpenSeadragon.

Python FastAPI Vue Docker License

Features

  • πŸ”¬ High-Performance Viewing: Smooth pan/zoom of gigapixel pathology images using OpenSeadragon
  • πŸ“ Smart Scale Bar: Automatic scale bar with Β΅m/mm measurements based on slide metadata
  • πŸ—‚οΈ File Browser: Hierarchical folder navigation with search and filtering
  • πŸ–ΌοΈ Thumbnails: Fast preview generation with viewport-aware lazy loading and concurrency limits
  • πŸ“Š Metadata Display: View slide properties, scanner info, resolution, and associated images
  • πŸš€ Redis Caching: Shared Redis backend for tiles, thumbnails, directory trees, and path resolution across workers
  • πŸ’Ύ Fallback Cache: Local pickle-based LRU path cache if Redis is disabled
  • 🎯 Modern Stack: Python 3.13, FastAPI, Vue.js 3, and containerized deployment
  • πŸ”’ Production Ready: Docker setup with health checks, non-root user, and optimized builds

Screenshots

Click to view screenshots

Grid View

Browse slides with thumbnails and file information

Grid View

Slide Viewer

Pan/zoom with scale bar, metadata panel, and associated images

Slide Viewer

Quick Start

Using Docker (Recommended)

  1. Clone the repository
git clone https://github.com/KatherLab/wsi-browser.git
cd wsi-browser
  1. Configure your slide directories

Edit docker-compose.yml to mount your slide directories:

volumes:
  - /path/to/your/slides:/path/to/your/slides:ro

Create a config.yml file (you can copy config.example.yml) to reference the mounted paths:

roots:
  - path: "/path/to/your/slides"
    label: "My Slides"
  1. Build and run
docker-compose build
docker-compose up -d
  1. Access the application Open your browser to: http://localhost:8010

Local Development

  1. Install dependencies with uv
pip install uv
uv sync
  1. Configure config.yml
roots:
  - path: "/path/to/slides"
    label: "Slide Collection"
    
cache:
  enabled: true
  redis_url: "redis://localhost:6379/0"
  1. Run the application
uv run uvicorn app.main:app --host 0.0.0.0 --port 8010 --reload

Configuration

config.yml Reference

# Slide directories to expose in the UI
roots:
  - path: "/data/slides"
    label: "Research Slides"
  - path: "/data/clinical"
    label: "Clinical Cases"

# Files/folders to exclude
exclude:
  - "__pycache__"
  - "*.tmp"
  - ".git"

# Supported slide formats
extensions:
  - ".svs"      # Aperio
  - ".tif"      # Generic TIFF
  - ".tiff"     
  - ".ndpi"     # Hamamatsu
  - ".scn"      # Leica
  - ".mrxs"     # Mirax (includes .mrxs sidecar directory)
  - ".bif"      # Ventana

# Redis caching configuration
cache:
  enabled: true
  redis_url: "redis://redis:6379/0"  # Use "redis" hostname in Docker
  ttl_seconds:
    tree: 60        # Directory tree cache
    thumb: 86400    # Thumbnail cache (24h)
    tile: 3600      # Tile cache (1h)

# Thumbnail generation
thumbnails:
  max_px: 512               # Maximum thumbnail dimension
  prefer_associated: true   # Use embedded thumbnails when available

# CORS settings
cors_allow_origins: ["*"]   # Restrict in production

Project Structure

wsi-browser/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ main.py           # FastAPI application
β”‚   β”œβ”€β”€ cache.py          # Redis caching layer
β”‚   β”œβ”€β”€ config.py         # Configuration management
β”‚   β”œβ”€β”€ dz.py             # Deep Zoom tile generation
β”‚   β”œβ”€β”€ fs_index.py       # File system indexing
β”‚   β”œβ”€β”€ models.py         # Pydantic models
β”‚   β”œβ”€β”€ thumbs.py         # Thumbnail generation
β”‚   β”œβ”€β”€ path_cache.py     # Redis + LRU path cache
β”‚   β”œβ”€β”€ templates/
β”‚   β”‚   └── index.html    # Vue.js frontend
β”‚   └── static/
β”‚       β”œβ”€β”€ logo.svg      # Optional branding
β”‚       └── logo.png
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ pyproject.toml
β”œβ”€β”€ config.yml
└── README.md

API Endpoints

Endpoint Description
GET / Web UI
GET /api/tree Directory tree structure
GET /api/expand?path=... Expand a directory shallowly
GET /api/dir?path=... List slides in directory
GET /api/thumb/{slide_id} Slide thumbnail (with ETag caching)
GET /api/meta/{slide_id} Slide metadata (with MIRAX sidecar size support)
GET /api/associated/{slide_id} List associated images
GET /api/associated/{slide_id}/{name} Get associated image
GET /dzi/{slide_id}.dzi Deep Zoom descriptor (with ETag)
GET /dzi/{slide_id}_files/{z}/{x}_{y}.jpeg Deep Zoom tiles (with ETag)
GET /health Health check (reports Redis status)

Supported Formats

The application supports all formats readable by OpenSlide:

  • Aperio (.svs, .tif)
  • Hamamatsu (.ndpi, .vms, .vmu)
  • Leica (.scn)
  • MIRAX (.mrxs + sidecar directory)
  • Philips (.tiff)
  • Sakura (.svslide)
  • Trestle (.tif)
  • Ventana (.bif, .tif)
  • Generic tiled TIFF (.tif, .tiff)

Performance Optimization

Caching Strategy

  • Redis: Stores tiles, thumbnails, directory trees, and path lookups (shared across workers)
  • LRU Fallback: Local pickle cache if Redis is disabled
  • TTL Configuration: Customizable expiration times for tiles and thumbnails
  • On-demand validation: Path entries checked for existence on access; stale entries evicted

Production Settings

  • Multiple Workers: 4+ Uvicorn workers recommended
  • Read-only Mounts: Slide directories mounted read-only
  • Health Checks: /health endpoint for container monitoring
  • Non-root User: Enhanced security in containers

Docker + Redis Best Practices

  • Set maxmemory and maxmemory-policy allkeys-lru in your Redis config to prevent out-of-memory errors:

    maxmemory 2gb
    maxmemory-policy allkeys-lru
    
  • Mount slides with :ro to enforce read-only access

  • Tune NFS mount options for throughput:

    nfsvers=3,rsize=262144,wsize=262144,hard,noatime
    

Troubleshooting

Slides not appearing

  • Check file extensions in config.yml
  • Verify directory permissions
  • Confirm Docker volume mounts

Performance issues

  • Increase Redis memory limit in docker-compose.yml
  • Adjust worker count based on CPU cores
  • Ensure NFS mount options are tuned (noatime, larger rsize/wsize)

Connection errors

docker-compose ps
docker-compose logs -f
docker-compose exec redis redis-cli ping
curl http://localhost:8010/health

Development

Adding Features

  • Backend: modify app/ Python modules
  • Frontend: edit app/templates/index.html
  • Rebuild: docker-compose build && docker-compose up -d

Tests & Linting

uv sync --dev
uv run pytest
uv run ruff check app/
uv run mypy app/

Deployment

Checklist

  • Restrict CORS origins in config.yml
  • Set up SSL/TLS termination (nginx/traefik)
  • Configure monitoring (Prometheus/Grafana)
  • Use log aggregation
  • Add authentication if needed
  • Set up Redis persistence/backups if required

Scaling

  • External Redis cluster for large deployments
  • Multiple app instances behind load balancer
  • CDN for static assets
  • Distributed file system (NFS/GlusterFS) for slides

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

Support

For issues and questions:

  • Open an issue on GitHub
  • Check existing issues for solutions
  • Provide logs and configuration when reporting bugs

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published