Kidney CT Classifier

Classifies a kidney CT slice into one of four classes: Cyst, Normal, Stone, Tumor.

The trained weights (~1 GB) are too large for GitHub, so they are hosted on the Hugging Face Hub:

FarhanKO/kidney-ct-classifier

These models are the image half of the kidney module in Disease-Risk-Prediction (kidney/image_based/). In the repo, this folder holds only this README, the OOD gate (kidney_ct_ood_detector.joblib) and metadata.json; the .keras files are git-ignored.

Research and education only. This is not a medical device. It was trained on 123 CT scans from hospitals in one city and must not be used for diagnosis or treatment decisions.

Quick start (no GitHub code needed)

pip install -r requirements.txt     # keras 3, torch, scikit-learn, numpy, pillow, huggingface_hub
from huggingface_hub import hf_hub_download
import importlib.util

REPO_ID = "FarhanKO/kidney-ct-classifier"
spec = importlib.util.spec_from_file_location("inference", hf_hub_download(REPO_ID, "inference.py"))
inference = importlib.util.module_from_spec(spec); spec.loader.exec_module(inference)

clf = inference.KidneyCTClassifier(REPO_ID)     # downloads metadata, OOD gate and DenseNet121_FT (~90 MB)
clf.predict("slice.png")
# {'status': 'accepted', 'label': 'Cyst', 'confidence': 0.943, 'p_abnormal': 0.988,
#  'probabilities': {'Cyst': 0.943, 'Normal': 0.012, 'Stone': 0.019, 'Tumor': 0.026}}

Or from the command line (--ensemble soft-votes the four fine-tuned models):

python inference.py slice1.png slice2.jpg --repo FarhanKO/kidney-ct-classifier

status is accepted (top-class probability β‰₯ 0.75), review (below 0.75: flag for a clinician) or rejected (the OOD gate says this is not a kidney CT slice; it is not classified). On the full test split, inference.py reproduces the notebook exactly: accuracy 0.8942, 3.64 % of slices rejected by the gate.

The OOD gate is a scikit-learn object saved with joblib, which is a pickle: load it only from a source you trust, and use scikit-learn 1.8 (pinned in requirements.txt).

Download into the GitHub repo

To use the full pipeline (with Grad-CAM) from a clone of the GitHub repo, put the files in this folder, where src/predict.py looks for them:

pip install huggingface_hub
from huggingface_hub import snapshot_download

# Everything (~1 GB): all nine checkpoints + gate + metadata
snapshot_download("FarhanKO/kidney-ct-classifier", local_dir="models")

# Only what the deployed cascade needs (~90 MB)
snapshot_download("FarhanKO/kidney-ct-classifier", local_dir="models",
                  allow_patterns=["DenseNet121_FT_best.keras", "kidney_ct_ood_detector.joblib", "metadata.json"])

Then, from kidney/image_based/:

python -m src.predict --image slice.png --heatmap-dir out/
python -m src.evaluate      # must reproduce the test metrics in metadata.json

Using a checkpoint directly

import os; os.environ["KERAS_BACKEND"] = "torch"   # before importing keras; trained on the torch backend
import keras
model = keras.models.load_model("DenseNet121_FT_best.keras", compile=False)
probs = model.predict(x)   # x: (N, 224, 224, 3) float32 in 0-255; classes in metadata.json order

Input preprocessing (the same as load_image() in src/data.py): grayscale β†’ pad to square with black β†’ resize to 224 Γ— 224 (PIL BOX) β†’ repeat to 3 channels β†’ float32 in 0–255. Do not divide by 255: every model rescales its input internally.

Files

File Size What it is
DenseNet121_FT_best.keras 84 MB Deployed model. ImageNet DenseNet121, fully fine-tuned
ResNet50V2_FT_best.keras 276 MB ResNet50V2, fine-tuned (ensemble member)
EfficientNetV2B0_FT_best.keras 72 MB EfficientNetV2B0, fine-tuned (ensemble member)
ConvNeXtTiny_FT_best.keras 321 MB ConvNeXtTiny, fine-tuned (ensemble member)
DenseNet121_best.keras Β· ResNet50V2_best.keras Β· EfficientNetV2B0_best.keras Β· ConvNeXtTiny_best.keras 31 / 97 / 27 / 109 MB Phase-1 checkpoints (frozen backbone, head only), for comparison
Custom_CNN_best.keras 5 MB Small CNN trained from scratch (baseline)
kidney_ct_ood_detector.joblib 4 MB Stage 1 OOD gate: Isolation Forest on DenseNet121_FT embeddings
metadata.json – Class order, input size, preprocessing, deployed model, ensemble members, confidence threshold, test metrics
inference.py – Stand-alone cascade (stages 1–3)
requirements.txt – Dependencies for inference.py

Every .keras file takes a (N, 224, 224, 3) input and outputs softmax probabilities for the 4 classes, in the order Cyst, Normal, Stone, Tumor.

How the cascade uses them

Stage What Outcome
1. OOD gate Isolation Forest on DenseNet121_FT embeddings Not a kidney CT slice β†’ rejected
2. Classification DenseNet121_FT Probabilities for the 4 classes
3. Confidence check Top-class probability < 0.75 review (flag for a clinician)
4. Explainability Grad-CAM on the top class Heatmap and overlay (in the GitHub repo)

Training data

CT KIDNEY DATASET: Normal-Cyst-Tumor and Stone (Islam et al., Scientific Reports 2022; Kaggle nazmul0087/ct-kidney-dataset-normal-cyst-tumor-and-stone), collected from hospital PACS in Dhaka, Bangladesh.

The 12,446 published images are consecutive slices of about 189 CT scans. 517 exact duplicates were removed, and the data was split by scan, so no scan appears in more than one split:

Split Slices Scans
Train 8,353 123
Val 1,789 30
Test 1,787 36

A random image split would leak near-identical neighbouring slices: a memorising 1-nearest-neighbour classifier scores 100 % on it, against 45 % on this split. The build script and a full manifest are in the GitHub repo under kidney/image_based/data/ (see its README.md).

Training

  • Framework: Keras 3 on the PyTorch backend (TensorFlow has no GPU support on native Windows), trained on an RTX 5060 (8 GB). Seed 42.
  • Backbones: ImageNet-pretrained ResNet50V2, EfficientNetV2B0, DenseNet121 and ConvNeXtTiny from Keras Applications, each applying its own input scaling, plus a small from-scratch CNN.
  • Phase 1: 5-epoch head warm-up with the backbone frozen.
  • Phase 2 (_FT): full fine-tuning of every layer, with BatchNorm frozen. AdamW (lr 3e-5, weight decay 0.05), label smoothing 0.1, class weights, and CT-safe augmentation (small rotation, zoom and translation; no flips).
  • Model selection: highest validation macro-F1, then lowest validation loss. The test set was not used for selection.

Results

Held-out test set: 1,787 slices from 36 scans never seen in training.

Model Val macro-F1 Test acc. Test macro-F1 Test macro ROC-AUC
Custom CNN 0.816 0.730 0.696 0.928
ResNet50V2 (fine-tuned) 0.844 0.868 0.826 0.973
EfficientNetV2B0 (fine-tuned) 0.817 0.806 0.737 0.965
DenseNet121 (fine-tuned) β€” deployed 0.889 0.894 0.867 0.976
ConvNeXtTiny (fine-tuned) 0.794 0.851 0.798 0.968
Soft-voting ensemble (4 Γ— FT) 0.868 0.877 0.841 0.983
Phase-1 (frozen backbone) models 0.53–0.70 0.56–0.64 0.47–0.53 0.76–0.87

Scan-level 95 % bootstrap CIs for the deployed model: accuracy 0.82–0.96, macro-F1 0.70–0.91.

Per class, deployed model, test set:

Class Precision Recall F1 Slices
Cyst 0.87 0.99 0.93 493
Normal 0.90 0.98 0.93 749
Stone 0.95 0.77 0.85 204
Tumor 0.90 0.66 0.76 341

Confidence check (0.75): 81 % of test slices are accepted, and 96.3 % of those are correct. The 19 % sent to review are 59.6 % correct.

OOD gate: rejects 3.6 % of real test slices, and 100 % of random noise, black images, grey speckle, chest X-rays and ECG printouts.

The ensemble is not deployed: it scores lower on validation (0.868 vs 0.889), and its tumor recall drops to 0.50.

Limitations

  • Few independent scans. Only 4 tumor scans each in val and test, so per-class tumor numbers move a lot with one scan. Tumor recall (0.66) is the weakest point.
  • Acquisition shortcuts. Body brightness differs by class (contrast phase, display window), and Grad-CAM often spreads over the central abdomen and spine, so part of the signal may be how the scan was taken.
  • One source, one format. All scans come from Dhaka hospitals, exported as windowed JPEGs, not Hounsfield-unit DICOM. Other scanners and windows are untested and may be rejected by the gate.
  • Single label per slice, and scans were grouped from image similarity because the dataset has no patient IDs.

Citation

If you use these models, please cite the dataset paper:

Islam, M. N., Hasan, M., et al. "Vision transformer and explainable transfer learning models for auto detection of kidney cyst, stone and tumor from CT-radiography." Scientific Reports 12, 11440 (2022).

Check the dataset's licence on its Kaggle page and set the model licence to match before publishing (the license: other field above).

Downloads last month
146
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Evaluation results

  • Test accuracy on CT KIDNEY DATASET Normal-Cyst-Tumor and Stone (deduplicated, scan-level test split)
    self-reported
    0.894
  • Test macro-F1 on CT KIDNEY DATASET Normal-Cyst-Tumor and Stone (deduplicated, scan-level test split)
    self-reported
    0.867
  • Test macro ROC-AUC on CT KIDNEY DATASET Normal-Cyst-Tumor and Stone (deduplicated, scan-level test split)
    self-reported
    0.976