A full-stack deep learning application that pairs a ResNet-50 classifier with a classical computer-vision pipeline (CLAHE enhancement, bilateral denoising, lung segmentation) behind a fast REST API and a clean React interface.
βΆ Click the preview to watch the full walkthrough: uploading chest X-rays and getting live predictions from the deployed app.
![]() Upload & Analyze workspace |
![]() X-ray selected, inference running |
![]() Pneumonia detected β 89.3% confidence |
![]() Normal lung β per-class probabilities |
Features Β· How it works Β· Getting started Β· API Β· Team
| Frontend | pulmo-net.vercel.app on Vercel |
| Backend | FastAPI + PyTorch (CPU) on Render |
| Demo video | youtu.be/DTXY-IxtPzg |
π€ The backend runs on a free tier that sleeps when idle. If the first prediction takes 30β60 seconds, it's waking up β after that, results come back in a few seconds.
Pneumonia is one of the leading causes of death worldwide, especially among children and the elderly, and chest X-rays are the frontline tool for diagnosing it. Reading them takes trained radiologists β who are scarce in many regions β and time that patients don't always have.
PulmoNet is our attempt at a second pair of eyes: a screening tool that flags likely pneumonia cases so they can be prioritised for expert review. It is built end to end β from image preprocessing and a trained CNN to a production-style API and a web interface anyone can use.
β οΈ PulmoNet is a research and educational project. It is not a medical device and must not be used as a substitute for diagnosis by a qualified clinician.
| π§ Deep learning inference | ResNet-50 backbone with a dropout + single-logit head, fine-tuned for binary Normal / Pneumonia classification |
| π¬ Medical-grade preprocessing | Min-max normalisation β resize to 256Γ256 β CLAHE contrast enhancement β bilateral denoising |
| π« Lung segmentation | Threshold + morphology + connected-component filtering isolates the lung fields from the spine, heart and shoulders |
| π Graceful fallback | If the model weights or PyTorch are unavailable, a feature-based classifier (intensity, histogram, texture, lung-area features) keeps the API serving |
| π¦ Batch analysis | Upload up to 10 X-rays at once, each scored independently with its own confidence |
| π±οΈ Drag-and-drop UI | Instant previews, per-image result cards, confidence bars and clear error states |
| π‘οΈ Input validation | Extension + MIME checks, 10 MB per-file cap, batch-size limits, sanitised error responses |
| βοΈ Swappable checkpoints | Two trained variants ship in the repo; choose one with a single env var |
| πͺΆ Lightweight deploys | CPU-only PyTorch build so the backend fits on free-tier hosts (β512 MB RAM) |
ββββββββββββββββ multipart/form-data βββββββββββββββββββββββββββββββββββββββββββββββββ
β React UI β βββββββββββββββββββββββββββΆ β FastAPI /api/predict β
β (Vite + β β β
β Tailwind) β βββββββββββββββββββββββββββ β 1. Validate type / size / count β
ββββββββββββββββ JSON predictions β 2. Decode β grayscale β
β 3. Normalise β 256Γ256 β CLAHE β bilateral β
β 4. Segment lungs (fallback-safe) β
β 5. ResNet-50 β sigmoid β probability β
β ββ no model? feature-based classifier β
βββββββββββββββββββββββββββββββββββββββββββββββββ
Model. A ResNet-50 (bottleneck blocks, layers 3-4-6-3) whose classifier head is replaced with Dropout(0.5) β Linear(2048, 1). The grayscale scan is replicated across three channels, passed through the network, and the sigmoid of the output logit gives the pneumonia probability. Predictions at or above 0.5 are labelled Pneumonia.
Checkpoints. Two trained weight files live in backend/models/:
| File | Description |
|---|---|
model_clahe.pth |
Trained on CLAHE-enhanced images (default, MODEL_VARIANT=clahe) |
best_model.pth |
Best checkpoint from the baseline training run |
The loader understands plain state_dicts, full pickled modules, and multi-variant checkpoint dictionaries, so you can drop in new weights without touching code.
PulmoNet/
βββ backend/
β βββ main.py # FastAPI app, CORS, logging, error handlers
β βββ schemas.py # Pydantic response models
β βββ routes/
β β βββ predict.py # /api/predict, /api/analyze, /api/predict/config, /api/health
β βββ services/
β β βββ xray_model.py # ResNet-50, preprocessing, lung segmentation, feature fallback
β β βββ model_loader.py # Singleton model loading + warm-up
β βββ utils/
β β βββ preprocess.py # Image validation & preprocessing helpers
β βββ models/ # Trained .pth checkpoints
β βββ test_model.py # Model integration tests
β βββ requirements.txt
β
βββ frontend/
βββ src/
β βββ components/ # Navbar, Hero, UploadArea, PredictionCard, β¦
β βββ services/apiService.js
β βββ App.jsx
β βββ index.jsx
βββ index.html
βββ vite.config.js
βββ tailwind.config.js
- Python 3.10+
- Node.js 18+ and npm
git clone https://github.com/variablePotato/PulmoNet.git
cd PulmoNetcd backend
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # optional: tweak MODEL_VARIANT, PORT, CORS
python main.pyThe API is now live at http://localhost:8000 β interactive docs at http://localhost:8000/docs.
cd frontend
npm install
cp .env.example .env # VITE_API_URL=http://localhost:8000/api
npm run devOpen http://localhost:3000, drop in a chest X-ray, and hit analyse.
| Variable | Where | Default | Purpose |
|---|---|---|---|
PORT |
backend | 8000 |
API port |
MODEL_VARIANT |
backend | clahe |
Which checkpoint variant to load |
VITE_API_URL |
frontend | http://localhost:8000/api |
Backend base URL |
VITE_API_TIMEOUT_MS |
frontend | 60000 |
Request timeout |
VITE_API_RETRIES |
frontend | 1 |
Retries on network failure |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/health |
Service and model status |
GET |
/api/predict/config |
Accepted formats and upload limits |
POST |
/api/predict |
Classify one or more X-rays |
POST |
/api/analyze |
Alias of /predict |
Example
curl -X POST http://localhost:8000/api/predict \
-F "files=@chest_xray_1.png" \
-F "files=@chest_xray_2.jpg"[
{
"filename": "chest_xray_1.png",
"prediction": "Pneumonia",
"confidence": 96.4,
"probability_normal": 0.036,
"probability_pneumonia": 0.964,
"status": "success"
}
]Limits: JPG / JPEG / PNG only Β· max 10 MB per file Β· max 10 files per request.
cd backend
python test_model.pyVerifies that the checkpoint loads, the preprocessing pipeline produces the expected tensor shape, and inference returns well-formed predictions.
Backend β any Python host works. The CPU-only PyTorch wheel keeps memory low enough for free tiers such as Render:
uvicorn main:app --host 0.0.0.0 --port $PORTFrontend β build static assets and serve them from Vercel, Netlify or any CDN:
npm run build # outputs to frontend/distRemember to set VITE_API_URL to your deployed backend and add the frontend origin to the backend's CORS list.
- Grad-CAM heatmaps to show where the model is looking
- Multi-class output (bacterial vs. viral pneumonia)
- Downloadable PDF screening report per scan
- Docker Compose setup for one-command local runs
- Published evaluation metrics (accuracy, sensitivity, specificity, AUC) on a held-out test set
PulmoNet was designed and built by:
| Name | GitHub | |
|---|---|---|
| π§βπ» | Aryan Raj | @ARYAN10227 |
| π§βπ» | Ayush Kumar | @variablePotato |
Found a bug or have an idea? Open an issue β contributions are welcome.
PulmoNet is intended for research and educational purposes only. Its outputs are probabilistic screening signals, not diagnoses. Every result must be reviewed by a qualified healthcare professional before any clinical decision is made.
Made with β and a lot of chest X-rays.



