A deep learning application that classifies grape leaf diseases using transfer learning. Trained on the PlantVillage dataset and deployed as an interactive web application with a REST API backend. The application provides disease classification, confidence scores, OpenCV based disease spot detection with contour outlining, and Grad-CAM++ heatmap visualization showing which regions of the leaf the model focused on when making its prediction.
Grape diseases cause significant crop losses worldwide and early detection is critical for effective treatment. Traditional disease identification relies on expert knowledge which is not always accessible to farmers especially in remote areas. Manual inspection is time consuming, subjective and prone to error.
This project addresses the problem by building an automated image classification system that can identify grape leaf diseases from a photograph. The goal is to provide an accurate, fast and accessible tool that any farmer or agricultural professional can use without specialist knowledge.
This project detects four grape leaf conditions from an uploaded image:
- Black rot
- Esca (Black Measles)
- Leaf blight (Isariopsis Leaf Spot)
- Healthy
The best model (DenseNet121 fine-tuned) achieves 99.34% test accuracy with only 4 mistakes out of 610 test images.
- Upload any grape leaf image or choose from built-in sample images
- Instant disease classification with confidence scores per class
- Disease spot detection using OpenCV color thresholding β outlines affected areas in red
- Grad-CAM++ heatmap visualization showing which regions the model focused on
- Split view showing original image alongside disease spots or Grad-CAM
- Single page layout with no scrolling required
- REST API with FastAPI for programmatic access
grape-disease-classifier/
βββ api/
β βββ main.py # FastAPI prediction endpoint
βββ frontend/
β βββ app.py # NiceGUI web interface
βββ src/
β βββ data/
β β βββ transforms.py # Image preprocessing pipeline
β βββ models/
β β βββ model.py # Model architecture definitions
β β βββ predict.py # Inference logic
β βββ utils/
β βββ gradcam.py # Grad-CAM++ heatmap generation
β βββ disease_detector.py # OpenCV disease spot detection
βββ test_images/ # Sample images for each class
βββ notebooks/ # Colab training notebooks
βββ models/ # Saved model weights (not in repo)
βββ Dockerfile # Container configuration
βββ requirements.txt # Python dependencies
βββ README.md
PlantVillage Dataset β grape subset only
| Class | Images |
|---|---|
| Black rot | 1180 |
| Esca (Black Measles) | 1383 |
| Leaf blight | 1076 |
| Healthy | 423 |
| Total | 4062 |
The dataset is split into 70% train, 15% validation and 15% test using stratified sampling to preserve class distribution across all splits.
Three pretrained ImageNet models were compared using transfer learning:
| Model | Val Accuracy | Fine-tuned Accuracy |
|---|---|---|
| EfficientNet-B0 | 97.87% | 98.52% |
| ResNet50 | 98.03% | 99.18% |
| DenseNet121 | 98.52% | 99.18% |
DenseNet121 was selected as the final model. It matches ResNet50 accuracy but has 3x fewer parameters (7M vs 23M), making it faster and lighter for deployment.
Training was done in two phases:
Phase 1 β Feature extraction: The backbone is frozen and only the classifier head (5K parameters) is trained for around 10 epochs. This gives the model a strong starting point quickly.
Phase 2 β Fine-tuning: The last 20% of backbone layers are unfrozen and trained with a much lower learning rate (0.00001) for around 15 epochs. This allows the model to adapt its high-level features specifically to grape disease patterns.
| Parameter | Value |
|---|---|
| Optimizer | Adam |
| Learning rate (feature extraction) | 0.001 |
| Learning rate (fine-tuning) | 0.00001 |
| Scheduler | CosineAnnealingLR |
| Batch size | 32 |
| Early stopping patience | 5 |
| Image size | 224x224 |
Confusion matrix on test set (610 images):
| Black rot | Esca | Leaf blight | Healthy | |
|---|---|---|---|---|
| Black rot | 175 | 2 | 0 | 0 |
| Esca | 2 | 206 | 0 | 0 |
| Leaf blight | 0 | 0 | 162 | 0 |
| Healthy | 0 | 0 | 0 | 63 |
Leaf blight and Healthy achieved zero mistakes. The only 4 errors occur between Black rot and Esca which are visually similar diseases that even human experts sometimes confuse.
OpenCV color thresholding detects and outlines disease spots directly on the leaf image. Dark brown and rust colored regions are identified and outlined in red with a semi-transparent fill showing the extent of infection.
Grad-CAM++ generates a heatmap showing which regions of the image the model focused on when making its prediction. Red areas indicate high attention. This confirms the model is learning genuine disease features rather than background artifacts.
Dataset scope: The model is trained exclusively on PlantVillage images taken under controlled laboratory conditions. Performance on real-world field photographs may be lower due to varying lighting, angles and image quality.
Class imbalance: The Healthy class has only 423 images compared to 1383 for Esca. Although stratified splitting and augmentation were used to mitigate this, the model has seen fewer examples of healthy leaves during training.
Limited disease coverage: The model only recognises four conditions. Other grape diseases such as powdery mildew or downy mildew are not covered. Uploading an image of an unrecognised disease will still produce a prediction for one of the four known classes.
No severity assessment: The model predicts the disease class but does not assess the severity or extent of infection on the leaf.
Single leaf input: The model expects a clear close-up image of a single leaf. Images containing multiple leaves, branches or other objects may produce unreliable predictions.
| Layer | Technology |
|---|---|
| Model training | PyTorch, torchvision |
| Disease spot detection | OpenCV |
| Model interpretability | Grad-CAM++ |
| API | FastAPI |
| Frontend | NiceGUI |
| Deployment | Hugging Face Spaces via Docker |
| Package management | uv |
- Python 3.12
- uv
git clone https://github.com/harmandeep2993/grape-disease-classifier
cd grape-disease-classifier
uv syncThe trained model is not included in this repository due to file size. Follow these steps to download it from the Hugging Face Space:
Option 1 β Manual download
- Go to the Hugging Face Space files
- Open the
modelsfolder - Click on
densenet121_finetuned_best.pth - Click the download button
- Place the file in your local
models/folder
Option 2 β Python script
Create a file called download_model.py in the project root:
from huggingface_hub import hf_hub_download
path = hf_hub_download(
repo_id="harman2993/grape-disease-classifier",
filename="models/densenet121_finetuned_best.pth",
repo_type="space",
local_dir="."
)
print(f"Model saved to: {path}")Then run:
uv run python download_model.pyCreate a .env file in the project root:
API_URL=http://127.0.0.1:8000Start the API in one terminal:
uv run uvicorn api.main:app --reloadStart the frontend in another terminal:
uv run python frontend/app.pyOpen http://localhost:8080 in your browser.
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check |
/predict |
POST | Upload image and get prediction |
curl -X POST "http://localhost:8000/predict" \
-F "file=@test_images/Healthy.jpg"{
"prediction": "healthy",
"confidence": 99.34,
"probabilities": {
"Black rot": 0.21,
"Esca (Black Measles)": 0.23,
"Leaf blight (Isariopsis Leaf Spot)": 0.22,
"healthy": 99.34
},
"spot_count": 0,
"annotated": "<base64 encoded PNG>",
"gradcam": "<base64 encoded PNG>"
}Build and run locally:
docker build -t grape-disease-classifier .
docker run -p 8080:8080 grape-disease-classifierHarmandeep Singh | Data Science and AI/ML




