Medical imaging case study

Colon Cancer Detection

Classifying colon tissue images as benign or adenocarcinoma with OpenCV contrast enhancement and a fine-tuned ResNet-18, packaged as a web demo anyone can try.

Project
Undergraduate project, rebuilt and extended in 2026
Data
LC25000, colon subset (10,000 images)
Stack
PyTorch, OpenCV, ResNet-18, Streamlit, Docker

The problem

Colorectal cancer is diagnosed by pathologists who examine stained tissue slides under a microscope. The work is slow, and different experts sometimes disagree on borderline cases. A model that flags likely cancer can help a pathologist prioritise which slides to review first.

The goal of this project is a binary classifier for colon tissue patches: benign or adenocarcinoma, the most common type of colon cancer.

The data

I used the colon part of the public LC25000 dataset: 10,000 H&E stained images at 768 × 768 pixels, 5,000 per class. The split is 70% training, 15% validation and 15% test, stratified so each split keeps the 50/50 balance.

One detail shaped how I read the results: the 10,000 images were created by augmenting 500 original images (250 per class). Augmented copies of the same original can land in both the training and test sets, which makes test scores optimistic. The dataset does not say which images share an original, so this cannot be fully fixed. I report it openly instead of hiding it.

Approach

  1. Contrast enhancement with OpenCVCLAHE on the lightness channel in LAB colour space sharpens nuclei and gland edges without changing the stain colours.
  2. AugmentationRandom flips, 90° rotations and small brightness and contrast changes. Tissue has no natural "up", and staining varies between labs.
  3. Transfer learningAn ImageNet-pretrained ResNet-18 with a new dropout and linear head, fine-tuned end to end with AdamW and a cosine learning-rate schedule.
  4. EvaluationAccuracy, precision, sensitivity, specificity, F1, ROC-AUC and a confusion matrix on the untouched test set.
  5. DeploymentA Streamlit app in a Docker container on Hugging Face Spaces, with sample images so visitors can try it in one click.

Key decisions

Reporting sensitivity and specificity, not just accuracy

In cancer screening, missing a cancer is worse than a false alarm. Accuracy hides that difference, so the evaluation reports sensitivity (how many cancers are caught) and specificity (how many benign samples are correctly cleared) separately, plus the ROC curve for choosing a different threshold.

Preprocessing for stain variation

H&E slides look different depending on the lab, the scanner and the staining batch. Enhancing contrast only on the lightness channel, together with mild brightness jitter during training, helps the model focus on tissue structure instead of colour shifts. The training script has a --no-clahe switch to measure what CLAHE contributes.

A model small enough to host for free

Free hosting means running on a CPU. ResNet-18 is accurate enough for this task and small enough (about 45 MB) to load and predict in about a second without a GPU, which keeps the live demo responsive.

Results

Trained for 8 epochs on a free Colab T4 GPU, then evaluated once on the 1,500 test images the model never saw during training (750 per class).

MetricScore
Accuracy100.0%
Sensitivity (cancers caught)100.0%
Specificity (benign correctly cleared)100.0%
Precision100.0%
F11.000
ROC-AUC1.000

Why I don't take 100% at face value

Validation accuracy hit 100% after the very first epoch. On a real-world medical task that would be a red flag, and here the cause is the dataset itself: the 10,000 colon images were made by rotating and flipping 500 originals, so copies of the same tissue sample sit in both the training and test sets. The test partly measures recognition of tissue the model has already seen. Published results on LC25000 commonly report 99 to 100% for the same reason.

A fair estimate needs a split that keeps near-duplicates together, or a test on slides from a different source. Both are on the list below.

The trained model, its metrics and sample images are published on Hugging Face, and the demo loads it from there.

Next steps