Every chest X-ray deserves a second look.
LungLens screens a chest X-ray for tuberculosis in about a second, sorting it into healthy, sick but not TB or TB, and lights up where in the lungs it found the evidence. The model runs in your browser, so the X-ray is never uploaded anywhere.
- 3-class accuracy
- 99.2%
- TB X-rays caught
- 200/200
- TB vs not-TB AUC
- 0.9996
- Where it runs
- your device
Try it on a real X-ray.
Upload a frontal chest X-ray (PNG or JPG), or tap one of the 12 test X-rays below. Each sample shows its true label; the dashed boxes on TB samples were drawn by doctors.
Impression
Probabilities
Research and teaching demo, not a medical device. A flagged X-ray needs a lab test; a clear one does not rule TB out. Not for children under 15.
The problem
Pakistan had about 670,000 TB cases in 2024, the fifth-highest burden in the world. Chest X-rays are cheap and available almost everywhere; people trained to read them for TB are not.
What LungLens does
- X-ray in → probabilities for healthy, sick-but-not-TB and TB, plus a heatmap.
- Screening rule: refer for a lab test if TB probability ≥ 0.7%. The threshold is fixed on validation data at 95% specificity; on the test set that catches 100.0% of TB cases, above WHO's 90% minimum for a triage test.
- Runs entirely in the browser (ONNX Runtime Web): works offline once loaded, costs nothing to serve, uploads nothing.
Why it is not a toy
- Most student TB projects use a dataset where TB and normal X-rays come from different hospitals, so a model can learn "which hospital" instead of "which disease".
- TBX11K's classes share sources, and its "sick but not TB" class forces the model to separate TB from pneumonia-like disease.
- We test where the model looks against boxes doctors drew around TB: the pointing game.
Dataset: TBX11K
Liu et al., Rethinking Computer-aided Tuberculosis Diagnosis, CVPR 2020. Simplified Kaggle release, licence CC BY 4.0. 512×512 chest X-rays with TB areas boxed by radiologists.
- Healthy
- Sick, not TB
- TB
| Split | Healthy | Sick, not TB | TB | Total |
|---|---|---|---|---|
| Train (90% of TBX11K train) | 2,700 | 2,700 | 539 | 5,939 |
| Validation (10%, stratified, seed 42) | 300 | 300 | 60 | 660 |
| Test (TBX11K's labelled validation set) | 800 | 800 | 200 | 1,800 |
Handling the data
- Grayscale, resized to 384×384 (TB lesions are small; 224 px loses them).
- TB is 9% of the training set: the loss weights TB about 5× more.
- Augmentation on the GPU: small rotations, shifts, zooms, exposure/contrast/gamma changes, noise, blur.
- No horizontal flips: a flipped X-ray puts the heart on the wrong side, which never happens in real life.
Models: one question at a time.
Three models, trained with identical data, augmentation, loss and schedule. The two DenseNets differ only in their starting weights, which isolates one question: does it help to start from a network that has already seen chest X-rays?
CNN from scratch
random weights
Baseline: 5 conv blocks (2.4M params). What deep learning does with no prior knowledge.
DenseNet-121
ImageNet (photos of objects)
Standard transfer learning; same architecture as Stanford's CheXNet. Deployed.
DenseNet-121
chest X-rays from 7 public datasets: NIH, CheXpert, MIMIC-CXR, PadChest and more (TorchXRayVision)
Domain-specific pretraining: has it learned what lungs look like?
Training
- AdamW, weight decay 0.05, cosine schedule with warm-up; 20 epochs (DenseNets), 40 (scratch).
- Batch 32, mixed precision, one NVIDIA L4 GPU on Google Colab.
- Best epoch chosen by validation balanced accuracy; test set never used for any choice.
Heatmaps without gradients
- Every model ends in global average pooling + one linear layer, so the class activation map (CAM, Zhou et al. 2016) comes out of the same forward pass.
- That is why the browser can draw the heatmap: the ONNX model returns probabilities and maps.
Results on 1,800 test X-rays
The deployed model is DenseNet-121 (ImageNet start), chosen on validation data. On the 1,800 untouched test X-rays it is 99.2% accurate across the three classes and separates TB from everything else with an AUC of 0.9996. At the screening threshold it catches 100.0% of TB cases (200 of 200). Starting from chest X-rays instead of ImageNet changed TB AUC by -0.0031 and balanced accuracy by -1.4 points.
200 of 200
TB X-rays flagged for a lab test. One dot per X-ray.
65 of 1,600
X-rays without TB referred anyway: the price of missing none.
- 3-class accuracy
- 99.2% 95% CI 98.8%–99.6%
- TB vs not-TB AUC
- 0.9996 95% CI 0.9991–0.9999
- TB sensitivity at the screening threshold
- 100.0% 95.9% specificity
- of TB X-rays: the heatmap's hottest point is inside a doctor's box
- 36% a random point: 10%
- CNN from scratchAUC 0.9952
- DenseNet-121 · ImageNet startAUC 0.9996
- DenseNet-121 · chest X-ray startAUC 0.9965
| Model | Params | Accuracy | Balanced acc. | TB AUC | Sensitivity | Specificity | Pointing game | CPU ms |
|---|---|---|---|---|---|---|---|---|
| CNN from scratch | 2.35M | 96.9% | 97.0% | 0.9952 | 98.0% | 94.6% | 34% | 246 |
| DenseNet-121 (ImageNet start) (deployed) | 6.96M | 99.2% | 98.4% | 0.9996 | 100.0% | 95.9% | 36% | 313 |
| DenseNet-121 (chest X-ray start) | 6.95M | 98.3% | 97.0% | 0.9965 | 98.0% | 95.8% | 57% | 294 |

Honest limits, and how to check us.
Honest limits
- TBX11K comes from Chinese hospitals; the model has not been tested on X-rays from Pakistani hospitals.
- Trained on adults; WHO's CAD recommendation is for people aged 15 and over.
- The screening threshold was set on validation X-rays from the same hospitals; a new population needs its own threshold (our first rule, set on only 60 validation TB X-rays, did not transfer to the test set).
- A heatmap shows where the evidence is, not a lesion outline; a high pointing-game score is not a diagnosis.
- The official TBX11K test labels are hidden; our test set is its labelled validation set, so numbers are not directly comparable to the leaderboard.
Test it yourself (3 commands)
git clone https://github.com/zyna-b/lunglens
cd lunglens && pip install -r requirements.txt
python evaluate.py # 12 bundled X-rays
python predict.py samples/tb_1.png --heatmaps out/
Every artefact, one click away.
For the grader and anyone else: every artefact of the project in one place. A machine-readable copy is at /project.json, and a plain-text guide for AI crawlers at /llms.txt.



