vivek12coder commited on
Commit
36dd4e6
·
1 Parent(s): 564f8b0

Initial commit - uploaded project

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.png filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Virtual environments
55
+ .env
56
+ .venv
57
+ env/
58
+ venv/
59
+ ENV/
60
+ env.bak/
61
+ venv.bak/
62
+
63
+ # IDEs and editors
64
+ .vscode/
65
+ .idea/
66
+ *.swp
67
+ *.swo
68
+ *~
69
+
70
+ # Jupyter Notebook
71
+ .ipynb_checkpoints
72
+
73
+ # Environment variables
74
+ .env
75
+ .env.local
76
+ .env.development.local
77
+ .env.test.local
78
+ .env.production.local
79
+
80
+ # Logs
81
+ *.log
82
+ logs/
83
+
84
+ # Machine Learning specific
85
+ # Large model files (uncomment if you want to exclude them)
86
+ # *.pth
87
+ # *.pt
88
+ # *.pkl
89
+ # *.h5
90
+ # *.onnx
91
+
92
+ # Data files (uncomment if you want to exclude large datasets)
93
+ # data/raw/
94
+ # data/processed/
95
+ # *.csv
96
+ # *.parquet
97
+
98
+ # Temporary files
99
+ *.tmp
100
+ *.temp
101
+ temp/
102
+ tmp/
103
+
104
+ # OS specific
105
+ # Windows
106
+ Thumbs.db
107
+ Thumbs.db:encryptable
108
+ ehthumbs.db
109
+ ehthumbs_vista.db
110
+ *.stackdump
111
+ [Dd]esktop.ini
112
+ $RECYCLE.BIN/
113
+ *.cab
114
+ *.msi
115
+ *.msix
116
+ *.msm
117
+ *.msp
118
+ *.lnk
119
+
120
+ # macOS
121
+ .DS_Store
122
+ .AppleDouble
123
+ .LSOverride
124
+ Icon
125
+ ._*
126
+ .DocumentRevisions-V100
127
+ .fseventsd
128
+ .Spotlight-V100
129
+ .TemporaryItems
130
+ .Trashes
131
+ .VolumeIcon.icns
132
+ .com.apple.timemachine.donotpresent
133
+ .AppleDB
134
+ .AppleDesktop
135
+
136
+ # Linux
137
+ *~
138
+ .fuse_hidden*
139
+ .directory
140
+ .Trash-*
141
+ .nfs*
142
+
143
+ # FastAPI specific
144
+ .pytest_cache/
145
+ *.pyc
146
+
147
+ # Docker
148
+ .dockerignore
149
+
150
+ # Uploaded files and temporary images
151
+ uploads/
152
+ temp_images/
153
+ *.jpg.tmp
154
+ *.png.tmp
155
+ *.jpeg.tmp
156
+
157
+ # Model checkpoints and training artifacts
158
+ checkpoints/
159
+ runs/
160
+ wandb/
161
+ tensorboard_logs/
162
+
163
+ # Output files that change frequently
164
+ outputs/temp/
165
+ outputs/cache/
166
+
167
+ # IDE specific files
168
+ .vscode/settings.json
169
+ .idea/workspace.xml
170
+ .idea/tasks.xml
DEPLOYMENT_GUIDE.md ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 FastAPI Crop Disease Detection - Deployment Guide
2
+
3
+ ## Overview
4
+
5
+ This project has been converted from Streamlit to FastAPI to provide a RESTful API for crop disease detection. The API includes all original features:
6
+
7
+ - **Health Check** - API and model status
8
+ - **Disease Prediction** - Image upload and AI inference
9
+ - **Grad-CAM Visualization** - Heat map generation
10
+ - **Progress Tracking** - Real-time processing status
11
+ - **Disease Information** - Knowledge base integration
12
+
13
+ ## 📋 API Endpoints
14
+
15
+ ### 1. Health Check
16
+ ```
17
+ GET /health
18
+ ```
19
+ Returns API status and model information.
20
+
21
+ ### 2. Disease Prediction
22
+ ```
23
+ POST /predict
24
+ ```
25
+ Upload an image file for disease prediction.
26
+
27
+ **Parameters:**
28
+ - `file` (required): Image file (JPG, PNG, BMP)
29
+ - `weather_data` (optional): JSON string with humidity, temperature, rainfall
30
+ - `include_gradcam` (optional): Generate Grad-CAM heatmap (default: true)
31
+ - `include_disease_info` (optional): Include disease information (default: true)
32
+
33
+ **Response:**
34
+ ```json
35
+ {
36
+ "success": true,
37
+ "predicted_class": "Tomato_Late_blight",
38
+ "crop": "Tomato",
39
+ "disease": "Late_blight",
40
+ "confidence": 0.95,
41
+ "all_probabilities": {...},
42
+ "risk_level": "High",
43
+ "processing_time": 2.3,
44
+ "task_id": "uuid-string"
45
+ }
46
+ ```
47
+
48
+ ### 3. Grad-CAM Visualization
49
+ ```
50
+ GET /gradcam/{task_id}
51
+ ```
52
+ Get the Grad-CAM heatmap for a prediction task.
53
+
54
+ ### 4. Processing Status
55
+ ```
56
+ GET /status/{task_id}
57
+ ```
58
+ Check the processing status of a task.
59
+
60
+ ### 5. Disease Information
61
+ ```
62
+ GET /disease-info?crop=Tomato&disease=Late_blight
63
+ ```
64
+ Get detailed information about a specific disease.
65
+
66
+ ## 🏃‍♂️ Local Development
67
+
68
+ ### Prerequisites
69
+ - Python 3.9+
70
+ - PyTorch
71
+ - FastAPI
72
+ - Uvicorn
73
+
74
+ ### Setup
75
+ 1. **Install dependencies:**
76
+ ```bash
77
+ pip install -r requirements.txt
78
+ ```
79
+
80
+ 2. **Run the API:**
81
+ ```bash
82
+ python app.py
83
+ ```
84
+ or
85
+ ```bash
86
+ uvicorn app:app --host 0.0.0.0 --port 7860
87
+ ```
88
+
89
+ 3. **Access the API:**
90
+ - API: http://localhost:7860
91
+ - Documentation: http://localhost:7860/docs
92
+ - Alternative docs: http://localhost:7860/redoc
93
+
94
+ ## 🤗 Hugging Face Spaces Deployment
95
+
96
+ ### Method 1: Web Interface
97
+
98
+ 1. **Create a new Space:**
99
+ - Go to https://huggingface.co/spaces
100
+ - Click "Create new Space"
101
+ - Choose "Docker" as the SDK
102
+ - Set visibility as desired
103
+
104
+ 2. **Upload files:**
105
+ - Upload all project files through the web interface
106
+ - Ensure `Dockerfile` is in the root directory
107
+
108
+ 3. **Build and deploy:**
109
+ - The Space will automatically build using the Dockerfile
110
+ - Check logs for any build issues
111
+
112
+ ### Method 2: Git Repository
113
+
114
+ 1. **Initialize git and add Hugging Face remote:**
115
+ ```bash
116
+ git init
117
+ git remote add origin https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
118
+ ```
119
+
120
+ 2. **Commit and push:**
121
+ ```bash
122
+ git add .
123
+ git commit -m "Initial FastAPI deployment"
124
+ git push origin main
125
+ ```
126
+
127
+ 3. **Monitor deployment:**
128
+ - Check the Space's build logs
129
+ - Verify the API is running at your Space URL
130
+
131
+ ## 📁 Project Structure
132
+
133
+ ```
134
+ diseases_aicrop/
135
+ ├── app.py # FastAPI application
136
+ ├── requirements.txt # Python dependencies
137
+ ├── Dockerfile # Container configuration
138
+ ├── DEPLOYMENT_GUIDE.md # This guide
139
+ ├── README.md # Project documentation
140
+ ├── src/ # Source code modules
141
+ │ ├── model.py # ResNet50 model definition
142
+ │ ├── explain.py # Grad-CAM explainer
143
+ │ ├── risk_level.py # Risk assessment
144
+ │ └── ...
145
+ ├── models/ # Trained models
146
+ │ └── crop_disease_v3_model.pth # Latest model (V3)
147
+ ├── knowledge_base/ # Disease information
148
+ │ └── disease_info.json
149
+ └── data/ # Training data (preserved)
150
+ ```
151
+
152
+ ## 🔧 Configuration
153
+
154
+ ### Environment Variables
155
+ - `PYTHONPATH`: Set to `/app` (handled by Dockerfile)
156
+ - `PYTHONDONTWRITEBYTECODE`: Prevents .pyc files
157
+ - `PYTHONUNBUFFERED`: Ensures proper logging
158
+
159
+ ### Model Configuration
160
+ The API automatically loads the latest model:
161
+ 1. `models/crop_disease_v3_model.pth` (preferred)
162
+ 2. `models/crop_disease_v2_model.pth` (fallback)
163
+
164
+ ## 🧪 Testing the API
165
+
166
+ ### Using curl
167
+ ```bash
168
+ # Health check
169
+ curl -X GET "http://localhost:7860/health"
170
+
171
+ # Predict disease
172
+ curl -X POST "http://localhost:7860/predict" \
173
+ -H "accept: application/json" \
174
+ -H "Content-Type: multipart/form-data" \
175
+ -F "file=@test_leaf_sample.jpg"
176
+ ```
177
+
178
+ ### Using Python requests
179
+ ```python
180
+ import requests
181
+
182
+ # Health check
183
+ response = requests.get("http://localhost:7860/health")
184
+ print(response.json())
185
+
186
+ # Predict disease
187
+ with open("test_leaf_sample.jpg", "rb") as f:
188
+ files = {"file": f}
189
+ response = requests.post("http://localhost:7860/predict", files=files)
190
+ print(response.json())
191
+ ```
192
+
193
+ ## 🐛 Troubleshooting
194
+
195
+ ### Common Issues
196
+
197
+ 1. **Model not loading:**
198
+ - Check if model files exist in `models/` directory
199
+ - Verify model file is not corrupted
200
+ - Check console logs for detailed error messages
201
+
202
+ 2. **CUDA/GPU issues:**
203
+ - API automatically falls back to CPU if CUDA unavailable
204
+ - For GPU deployment, ensure CUDA-compatible PyTorch version
205
+
206
+ 3. **Memory issues:**
207
+ - Increase container memory limits if needed
208
+ - Monitor memory usage during inference
209
+
210
+ 4. **Port conflicts:**
211
+ - Ensure port 7860 is available
212
+ - Modify port in Dockerfile and uvicorn command if needed
213
+
214
+ ### Debugging
215
+ - Check FastAPI logs in the console
216
+ - Use `/health` endpoint to verify model status
217
+ - Access `/docs` for interactive API testing
218
+
219
+ ## 🚀 Performance Optimization
220
+
221
+ ### Production Recommendations
222
+ - Use GPU-enabled containers for faster inference
223
+ - Implement caching for repeated requests
224
+ - Add rate limiting for production use
225
+ - Monitor API performance and add logging
226
+
227
+ ### Scaling
228
+ - Deploy multiple instances behind a load balancer
229
+ - Use Redis for shared processing status storage
230
+ - Implement background task queues for heavy operations
231
+
232
+ ## 📊 Monitoring
233
+
234
+ ### Health Monitoring
235
+ - The `/health` endpoint provides model status
236
+ - Docker health check verifies API availability
237
+ - Monitor response times and error rates
238
+
239
+ ### Logging
240
+ - All processing steps are logged to console
241
+ - Error messages include detailed stack traces
242
+ - Task IDs help track individual requests
243
+
244
+ ## 🔄 Updates and Maintenance
245
+
246
+ ### Model Updates
247
+ 1. Replace model file in `models/` directory
248
+ 2. Update DEFAULT_CLASSES if class changes occurred
249
+ 3. Restart the API to load new model
250
+
251
+ ### Code Updates
252
+ 1. Update application code
253
+ 2. Test locally before deployment
254
+ 3. Push changes to trigger automatic rebuild
255
+
256
+ ## 📞 Support
257
+
258
+ For issues and questions:
259
+ - Check this deployment guide
260
+ - Review the API documentation at `/docs`
261
+ - Examine console logs for error details
262
+ - Test endpoints individually to isolate problems
263
+
264
+ ---
265
+
266
+ 🌱 **Happy deploying!** Your FastAPI-based crop disease detection system is ready for production use.
DEPLOYMENT_SUMMARY.md ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 Hugging Face Spaces Deployment - Deliverables Summary
2
+
3
+ This document summarizes all deliverables for deploying the Crop Disease Detection AI on Hugging Face Spaces.
4
+
5
+ ## 📦 Deliverables Completed
6
+
7
+ ### 1. ✅ Production Dockerfile
8
+ **File**: `Dockerfile`
9
+ - **Base Image**: `python:3.9-slim` (optimized for Hugging Face Spaces)
10
+ - **Port**: 7860 (HF Spaces standard)
11
+ - **Dependencies**: Streamlit + PyTorch + Grad-CAM
12
+ - **Health Check**: Included for monitoring
13
+ - **Environment**: Production-ready with proper paths
14
+
15
+ ### 2. ✅ Optimized requirements.txt
16
+ **File**: `requirements.txt` (renamed from `requirements_streamlit.txt`)
17
+ - **Core ML**: torch==2.1.0, torchvision==0.16.0
18
+ - **Web Framework**: streamlit>=1.28.0
19
+ - **Image Processing**: opencv-python-headless (headless for Docker)
20
+ - **Visualization**: grad-cam>=1.4.8, matplotlib>=3.7.0
21
+ - **Utilities**: numpy, requests, tqdm, pydantic
22
+
23
+ ### 3. ✅ Streamlit Application
24
+ **File**: `app.py`
25
+ - **Model Loading**: Supports both V3 and V2 models with fallback
26
+ - **Image Upload**: Drag & drop interface with multiple formats
27
+ - **AI Prediction**: Disease classification with confidence scores
28
+ - **Grad-CAM Visualization**: Visual explanations of AI decisions
29
+ - **Disease Information**: Comprehensive symptoms & treatment details
30
+ - **Risk Assessment**: Environmental factor integration
31
+ - **Responsive UI**: Mobile-friendly with sidebar settings
32
+
33
+ ### 4. ✅ Project Cleanup
34
+ **Log File**: `deployment_cleanup_log.txt`
35
+
36
+ **Removed Files (12 items)**:
37
+ - `api/` - FastAPI components (not needed for Streamlit)
38
+ - `tests/` - Test files (not needed in production)
39
+ - `.vscode/` - IDE configuration
40
+ - `crop_disease_gui.py` - GUI app (replaced by Streamlit)
41
+ - `outputs/*.png` - Training visualization files
42
+ - `outputs/training_*.json` - Training result files
43
+ - `outputs/heatmaps/` - Temporary heatmap directory
44
+ - `TRAINING_REPORT.md` - Development documentation
45
+
46
+ **Preserved Essential Files**:
47
+ - ✅ `src/` - All core ML modules
48
+ - ✅ `models/` - Both V2 and V3 model files (200MB total)
49
+ - ✅ `knowledge_base/` - Disease information database
50
+ - ✅ `notebooks/` - Training notebooks for retraining
51
+ - ✅ `data/` - Dataset structure (empty directories preserved)
52
+ - ✅ `test_leaf_sample.jpg` - Sample test image
53
+
54
+ ### 5. ✅ Deployment Documentation
55
+ **Files**:
56
+ - `DEPLOY_INSTRUCTIONS.md` - Comprehensive deployment guide
57
+ - `README.md` - Updated with deployment information
58
+
59
+ **Contents**:
60
+ - Step-by-step Hugging Face Spaces deployment
61
+ - Local testing instructions (Python + Docker)
62
+ - Verification checklist with sample test cases
63
+ - Troubleshooting guide with common issues
64
+ - Performance expectations and hardware recommendations
65
+
66
+ ### 6. ✅ Verification System
67
+ **File**: `verify_deployment.py`
68
+ - **Import Testing**: Verifies all dependencies available
69
+ - **File Structure**: Confirms all required files present
70
+ - **Model Validation**: Checks model files exist and are valid size
71
+ - **App Structure**: Validates Streamlit app components
72
+ - **Dockerfile**: Confirms Docker configuration correct
73
+ - **Module Testing**: Tests src/ module imports
74
+
75
+ **Last Test Result**: ✅ 6/6 tests passed - Ready for deployment!
76
+
77
+ ## 🎯 Acceptance Criteria Status
78
+
79
+ ### ✅ Docker Image Requirements
80
+ - [x] Dockerfile builds without errors
81
+ - [x] `streamlit run` serves the UI properly
82
+ - [x] Uses python:3.9-slim base image optimized for HF Spaces
83
+ - [x] Exposes port 7860 (HF Spaces standard)
84
+ - [x] Includes health check endpoint
85
+
86
+ ### ✅ Model Integration
87
+ - [x] App loads model from `models/` folder
88
+ - [x] Supports both V3 (primary) and V2 (fallback) models
89
+ - [x] Returns correct predictions with confidence scores
90
+ - [x] Generates Grad-CAM heatmaps for visual explanations
91
+ - [x] Handles model loading errors gracefully
92
+
93
+ ### ✅ File Management
94
+ - [x] All removed files documented with justifications
95
+ - [x] Essential training components preserved:
96
+ - Model files (.pth)
97
+ - Training scripts (src/)
98
+ - Training notebooks
99
+ - Dataset structure
100
+ - Knowledge base
101
+ - [x] No critical functionality lost
102
+
103
+ ### ✅ Deployment Instructions
104
+ - [x] Step-by-step HF Spaces deployment guide
105
+ - [x] Local testing instructions (Python + Docker)
106
+ - [x] Verification checklist included
107
+ - [x] Sample test cases documented
108
+ - [x] Troubleshooting section comprehensive
109
+
110
+ ## 🧪 Testing Summary
111
+
112
+ ### Local Testing Results
113
+ - **Dependencies**: ✅ All imports successful
114
+ - **File Structure**: ✅ All required files present
115
+ - **Model Files**: ✅ V2 (100.1MB) and V3 (100.1MB) valid
116
+ - **App Structure**: ✅ All Streamlit components functional
117
+ - **Dockerfile**: ✅ Configuration correct
118
+ - **Source Modules**: ✅ All src/ modules importable
119
+
120
+ ### Sample Test Cases Verified
121
+ 1. **Image Upload**: ✅ Supports JPG, PNG, BMP formats
122
+ 2. **Disease Prediction**: ✅ Returns class + confidence
123
+ 3. **Grad-CAM**: ✅ Generates visual explanations
124
+ 4. **Disease Info**: ✅ Shows symptoms & treatments
125
+ 5. **Settings**: ✅ Sidebar controls functional
126
+
127
+ ## 📊 Production Readiness
128
+
129
+ ### Performance Expectations
130
+ - **Build Time**: 5-10 minutes on HF Spaces
131
+ - **Model Loading**: 10-30 seconds
132
+ - **Prediction Time**: 2-5 seconds (CPU), 1-2 seconds (GPU)
133
+ - **Memory Usage**: ~2-4GB RAM
134
+ - **Disk Usage**: ~1-2GB
135
+
136
+ ### Scalability Features
137
+ - **Caching**: @st.cache_resource for model loading
138
+ - **Error Handling**: Graceful fallbacks for missing components
139
+ - **Responsive Design**: Works on mobile and desktop
140
+ - **Resource Optimization**: Headless OpenCV, minimal dependencies
141
+
142
+ ## 🚀 Next Steps
143
+
144
+ 1. **Upload to HF Spaces**: Use git or web interface
145
+ 2. **Monitor Build**: Check logs for any issues
146
+ 3. **Test Live App**: Verify all functionality works
147
+ 4. **Share & Iterate**: Collect feedback and improve
148
+
149
+ ## 📋 Quick Deployment Checklist
150
+
151
+ - [ ] Create new HF Space with Docker SDK
152
+ - [ ] Upload all project files
153
+ - [ ] Wait for build completion (5-10 min)
154
+ - [ ] Test image upload functionality
155
+ - [ ] Verify disease predictions work
156
+ - [ ] Confirm Grad-CAM visualizations appear
157
+ - [ ] Check disease information displays
158
+ - [ ] Share live app URL
159
+
160
+ ---
161
+
162
+ **🎉 Deployment Package Ready!**
163
+
164
+ All components are tested, verified, and ready for production deployment on Hugging Face Spaces. The system is optimized for both functionality and performance in a cloud environment.
Dockerfile ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FastAPI Dockerfile for Hugging Face Spaces
2
+ # Optimized for crop disease detection API deployment
3
+
4
+ FROM python:3.9-slim
5
+
6
+ # Set working directory
7
+ WORKDIR /app
8
+
9
+ # Set environment variables
10
+ ENV PYTHONPATH="/app"
11
+ ENV PYTHONDONTWRITEBYTECODE=1
12
+ ENV PYTHONUNBUFFERED=1
13
+
14
+ # Install system dependencies
15
+ RUN apt-get update && apt-get install -y \
16
+ libglib2.0-0 \
17
+ libsm6 \
18
+ libxext6 \
19
+ libxrender-dev \
20
+ libgomp1 \
21
+ libglib2.0-0 \
22
+ libgtk-3-0 \
23
+ libgl1-mesa-glx \
24
+ curl \
25
+ && rm -rf /var/lib/apt/lists/*
26
+
27
+ # Copy requirements first for better caching
28
+ COPY requirements.txt requirements.txt
29
+
30
+ # Install Python dependencies
31
+ RUN pip install --no-cache-dir --upgrade pip && \
32
+ pip install --no-cache-dir -r requirements.txt
33
+
34
+ # Copy application files
35
+ COPY app.py .
36
+ COPY src/ ./src/
37
+ COPY models/ ./models/
38
+ COPY knowledge_base/ ./knowledge_base/
39
+ COPY test_leaf_sample.jpg .
40
+
41
+ # Create necessary directories
42
+ RUN mkdir -p outputs temp
43
+
44
+ # Expose the port FastAPI runs on
45
+ EXPOSE 7860
46
+
47
+ # Health check
48
+ HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
49
+ CMD curl -f http://localhost:7860/health || exit 1
50
+
51
+ # Command to run the FastAPI app
52
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,558 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
- title: Crop
3
- emoji: 🏃
4
- colorFrom: red
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🌱 Crop Disease Detection API
2
+
3
+ [![Python](https://img.shields.io/badge/Python-3.9%2B-blue.svg)](https://python.org)
4
+ [![PyTorch](https://img.shields.io/badge/PyTorch-2.1.0-red.svg)](https://pytorch.org)
5
+ [![FastAPI](https://img.shields.io/badge/FastAPI-0.104.0-green.svg)](https://fastapi.tiangolo.com)
6
+ [![Hugging Face Spaces](https://img.shields.io/badge/🤗%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces)
7
+ [![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
+
9
+ A RESTful API for AI-powered crop disease detection using deep learning to identify diseases in pepper, potato, and tomato crops from leaf images. The API provides accurate disease classification, risk assessment, Grad-CAM visualizations, and treatment recommendations.
10
+
11
+ > **🚀 Production Ready**: FastAPI-based REST API optimized for Hugging Face Spaces deployment with Docker. All features preserved from the original Streamlit implementation.
12
+
13
+ ## 🎯 API Overview
14
+
15
+ This FastAPI service provides a comprehensive crop disease detection pipeline that:
16
+ - **Detects 15 different diseases** across pepper, potato, and tomato crops
17
+ - **Provides visual explanations** using Grad-CAM heatmaps
18
+ - **Offers treatment recommendations** from an integrated knowledge base
19
+ - **Calculates risk levels** based on confidence and environmental factors
20
+ - **RESTful endpoints** for health checks, predictions, visualizations, and status tracking
21
+ - **🚀 Deployment Ready**: Optimized for Hugging Face Spaces with Docker support
22
+
23
+ ### 🏆 Key Features
24
+
25
+ - **🤖 AI Model**: ResNet50-based transfer learning with 26.1M parameters (V3)
26
+ - **📊 Disease Classes**: 15 classes including healthy variants for each crop
27
+ - **🎨 Visual Explanations**: Grad-CAM heatmaps highlighting infected regions
28
+ - **📚 Knowledge Base**: Comprehensive disease information with symptoms and treatments
29
+ - **⚡ Real-time Processing**: Fast inference with GPU/CPU support and progress tracking
30
+ - **🌐 REST API**: FastAPI with automatic OpenAPI documentation
31
+ - **🖥️ CLI Tool**: Command-line interface for batch processing (preserved)
32
+ - **📓 Training Pipeline**: Complete model training and evaluation system (preserved)
33
+
34
+ ## 📁 Project Structure
35
+
36
+ ```
37
+ diseases_aicrop/
38
+ ├── � app.py # FastAPI application (main API server)
39
+ ├── 📄 requirements.txt # Python dependencies (FastAPI + ML)
40
+ ├── 📄 Dockerfile # Docker container configuration
41
+ ├── 📄 DEPLOYMENT_GUIDE.md # Detailed deployment instructions
42
+ ├── 📄 README.md # This file
43
+ ├── 📂 src/ # Core modules
44
+ │ ├── model.py # ResNet50 model definition
45
+ │ ├── explain.py # Grad-CAM explainer
46
+ │ ├── risk_level.py # Risk assessment calculator
47
+ │ ├── predict_cli.py # CLI tool (preserved)
48
+ │ ├── train.py # Model training (preserved)
49
+ │ └── evaluate.py # Model evaluation (preserved)
50
+ ├── 📂 models/ # Trained model weights
51
+ │ ├── crop_disease_v3_model.pth # Latest V3 model (preferred)
52
+ │ └── crop_disease_v2_model.pth # V2 model (fallback)
53
+ ├── 📂 knowledge_base/ # Disease information database
54
+ │ └── disease_info.json # Symptoms, treatments, prevention
55
+ ├── 📂 notebooks/ # Training and analysis (preserved)
56
+ │ └── train_resnet50.ipynb # Model training notebook
57
+ ├── 📂 data/ # Dataset (preserved for retraining)
58
+ │ └── raw/ # Original dataset
59
+ └── 📂 outputs/ # Evaluation results (preserved)
60
+ ├── 📂 notebooks/ # Jupyter notebooks
61
+ │ └── train_resnet50.ipynb # Training notebook
62
+ ├── 📂 outputs/ # Results and visualizations
63
+ │ ├── heatmaps/ # Grad-CAM visualizations
64
+ │ └── *.json # Evaluation results
65
+ ├── 📂 src/ # Core source code
66
+ │ ├── dataset.py # Data loading and preprocessing
67
+ │ ├── model.py # ResNet50 architecture
68
+ │ ├── train.py # Training pipeline
69
+ │ ├── evaluate.py # Model evaluation
70
+ │ ├── explain.py # Grad-CAM explanations
71
+ │ ├── risk_level.py # Risk assessment logic
72
+ │ └── predict_cli.py # CLI predictor
73
+ ├── 📂 tests/ # Unit tests
74
+ ├── crop_disease_gui.py # Tkinter GUI application
75
+ ├── requirements.txt # Main dependencies
76
+ └── TRAINING_REPORT.md # Performance analysis
77
+ ```
78
+
79
+ ## 🛠️ Technology Stack
80
+
81
+ ### Core Technologies
82
+ - **Deep Learning**: PyTorch 2.1.0, torchvision 0.16.0
83
+ - **Model Architecture**: ResNet50 with transfer learning
84
+ - **Web Framework**: Streamlit 1.28.0+
85
+ - **Computer Vision**: OpenCV, PIL/Pillow
86
+ - **Visualization**: Grad-CAM, matplotlib
87
+
88
+ ### Dependencies
89
+ - **Core ML**: PyTorch, torchvision, numpy
90
+ - **Image Processing**: OpenCV-Python, Pillow
91
+ - **Web Interface**: Streamlit
92
+ - **Visualization**: matplotlib, grad-cam
93
+ - **Utilities**: requests, tqdm, pydantic
94
+
95
+ ### Development Tools
96
+ - **Environment**: Python 3.9+ (Docker: python:3.9-slim)
97
+ - **Notebooks**: Jupyter/Google Colab support
98
+ - **Deployment**: Docker + Hugging Face Spaces
99
+ - **Version Control**: Git
100
+ - **Local Development**: Optimized for Windows PowerShell
101
+
102
+ ## 🚀 Installation & Setup
103
+
104
+ ### Prerequisites
105
+ - Python 3.8 or higher
106
+ - pip package manager
107
+ - (Optional) CUDA-compatible GPU for faster training
108
+
109
+ ### 1. Clone Repository
110
+ ```bash
111
+ git clone https://github.com/vivek12coder/AiCropDiseasesDetection.git
112
+ cd AiCropDiseasesDetection
113
+ ```
114
+
115
+ ### 2. Create Virtual Environment
116
+ ```powershell
117
+ # Windows PowerShell (recommended)
118
+ python -m venv .venv
119
+ .\.venv\Scripts\Activate.ps1
120
+
121
+ # Alternative for Command Prompt
122
+ python -m venv .venv
123
+ .venv\Scripts\activate.bat
124
+
125
+ # macOS/Linux
126
+ python -m venv .venv
127
+ source .venv/bin/activate
128
+ ```
129
+
130
+ ### 3. Install Dependencies
131
+ ```powershell
132
+ # Install main dependencies
133
+ pip install -r requirements.txt
134
+
135
+ # For API development (optional)
136
+ pip install -r api/requirements.txt
137
+ ```
138
+
139
+ ### 4. Pre-trained Model
140
+ The repository includes the latest pre-trained model:
141
+ - `models/crop_disease_v3_model.pth` - Latest V3 model (recommended)
142
+
143
+ > **Note**: Older model versions have been removed to keep the project clean. Only the latest, best-performing model is included.
144
+
145
+ ### 5. Verify Installation
146
+ ```bash
147
+ python -c "import torch; print(f'PyTorch: {torch.__version__}')"
148
+ python -c "import torchvision; print(f'TorchVision: {torchvision.__version__}')"
149
+ ```
150
+
151
+ ## 📖 API Usage Guide
152
+
153
+ ### 🚀 Quick Start
154
+
155
+ Start the FastAPI server locally:
156
+
157
+ ```powershell
158
+ # Run the FastAPI application
159
+ python app.py
160
+ ```
161
+
162
+ The API will be available at:
163
+ - **API Base URL**: http://localhost:7860
164
+ - **Interactive Docs**: http://localhost:7860/docs
165
+ - **Alternative Docs**: http://localhost:7860/redoc
166
+
167
+ ### 🌐 API Endpoints
168
+
169
+ #### 1. Health Check
170
+ Check API and model status:
171
+ ```bash
172
+ curl -X GET "http://localhost:7860/health"
173
+ ```
174
+
175
+ **Response:**
176
+ ```json
177
+ {
178
+ "status": "healthy",
179
+ "model_loaded": true,
180
+ "model_version": "crop_disease_v3_model.pth",
181
+ "available_endpoints": ["/health", "/predict", "/gradcam/{task_id}", "/status/{task_id}"],
182
+ "timestamp": "2024-01-01T12:00:00",
183
+ "device": "cuda:0"
184
+ }
185
+ ```
186
+
187
+ #### 2. Disease Prediction
188
+ Upload an image for disease detection:
189
+ ```bash
190
+ curl -X POST "http://localhost:7860/predict" \
191
+ -H "Content-Type: multipart/form-data" \
192
+ -F "file=@test_leaf_sample.jpg" \
193
+ -F "include_gradcam=true"
194
+ ```
195
+
196
+ **Response:**
197
+ ```json
198
+ {
199
+ "success": true,
200
+ "predicted_class": "Tomato_Late_blight",
201
+ "crop": "Tomato",
202
+ "disease": "Late_blight",
203
+ "confidence": 0.95,
204
+ "all_probabilities": {
205
+ "Tomato_Late_blight": 0.95,
206
+ "Tomato_Early_blight": 0.03,
207
+ "Tomato_healthy": 0.02
208
+ },
209
+ "risk_level": "High",
210
+ "processing_time": 2.3,
211
+ "task_id": "550e8400-e29b-41d4-a716-446655440000"
212
+ }
213
+ ```
214
+
215
+ #### 3. Grad-CAM Visualization
216
+ Get the heatmap for a prediction:
217
+ ```bash
218
+ curl -X GET "http://localhost:7860/gradcam/550e8400-e29b-41d4-a716-446655440000"
219
+ ```
220
+
221
+ **Response:**
222
+ ```json
223
+ {
224
+ "success": true,
225
+ "heatmap_base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
226
+ "explanation": "Grad-CAM heatmap showing areas the AI model focused on for prediction",
227
+ "task_id": "550e8400-e29b-41d4-a716-446655440000",
228
+ "processing_time": 1.2
229
+ }
230
+ ```
231
+
232
+ #### 4. Processing Status
233
+ Track processing progress:
234
+ ```bash
235
+ curl -X GET "http://localhost:7860/status/550e8400-e29b-41d4-a716-446655440000"
236
+ ```
237
+
238
+ **Response:**
239
+ ```json
240
+ {
241
+ "task_id": "550e8400-e29b-41d4-a716-446655440000",
242
+ "status": "completed",
243
+ "progress": 100,
244
+ "message": "Analysis completed successfully",
245
+ "timestamp": "2024-01-01T12:00:30"
246
+ }
247
+ ```
248
+
249
+ #### 5. Disease Information
250
+ Get detailed disease information:
251
+ ```bash
252
+ curl -X GET "http://localhost:7860/disease-info?crop=Tomato&disease=Late_blight"
253
+ ```
254
+
255
+ ### � Python Client Example
256
+
257
+ ```python
258
+ import requests
259
+ import json
260
+ from PIL import Image
261
+ import base64
262
+ import io
263
+
264
+ # API base URL
265
+ API_BASE = "http://localhost:7860"
266
+
267
+ # 1. Health check
268
+ response = requests.get(f"{API_BASE}/health")
269
+ print("Health Check:", response.json())
270
+
271
+ # 2. Predict disease
272
+ with open("test_leaf_sample.jpg", "rb") as f:
273
+ files = {"file": f}
274
+ data = {
275
+ "weather_data": json.dumps({
276
+ "humidity": 70.0,
277
+ "temperature": 22.0,
278
+ "rainfall": 5.0
279
+ }),
280
+ "include_gradcam": True
281
+ }
282
+ response = requests.post(f"{API_BASE}/predict", files=files, data=data)
283
+ prediction = response.json()
284
+
285
+ print("Prediction:", prediction)
286
+ task_id = prediction["task_id"]
287
+
288
+ # 3. Get Grad-CAM visualization
289
+ import time
290
+ time.sleep(2) # Wait for background processing
291
+ response = requests.get(f"{API_BASE}/gradcam/{task_id}")
292
+ if response.status_code == 200:
293
+ gradcam = response.json()
294
+ # Decode and display heatmap
295
+ heatmap_data = base64.b64decode(gradcam["heatmap_base64"].split(",")[1])
296
+ heatmap_image = Image.open(io.BytesIO(heatmap_data))
297
+ heatmap_image.show()
298
+
299
+ # 4. Get disease information
300
+ crop = prediction["crop"]
301
+ disease = prediction["disease"]
302
+ response = requests.get(f"{API_BASE}/disease-info", params={"crop": crop, "disease": disease})
303
+ disease_info = response.json()
304
+ print("Disease Info:", disease_info)
305
+ ```
306
+
307
+ ### 🖥️ CLI Tool (Preserved)
308
+
309
+ For batch processing or scripting:
310
+
311
+ ```powershell
312
+ # Single image prediction
313
+ python -m src.predict_cli -i test_leaf_sample.jpg -m models\crop_disease_v3_model.pth
314
+
315
+ # With custom settings
316
+ python -m src.predict_cli -i your_image.jpg --output-dir results/
317
+ ```
318
+
319
+ ### 📊 Model Training & Evaluation (Preserved)
320
+
321
+ Original training and evaluation capabilities remain intact:
322
+
323
+ ```powershell
324
+ # Evaluate existing model
325
+ python -m src.evaluate
326
+
327
+ # Train new model
328
+ python -m src.train
329
+
330
+ # Generate visual explanations
331
+ python -m src.explain
332
+ ```
333
+
334
+ ### � Jupyter Notebooks (Preserved)
335
+
336
+ Explore the training process interactively:
337
+
338
+ ```powershell
339
+ jupyter notebook notebooks/train_resnet50.ipynb
340
+ ```
341
+ 4. **View Results**: See detailed analysis in results panel
342
+
343
+ ## 🎯 Model Performance
344
+
345
+ ### Current Performance (V3 Model)
346
+ - **Model Architecture**: ResNet50 with custom classifier layers
347
+ - **Parameters**: 26.1M total parameters
348
+ - **Input Size**: 224x224 RGB images
349
+ - **Classes**: 15 disease classes across 3 crops
350
+ - **Inference Speed**: ~0.1 seconds per image on CPU
351
+
352
+ ### Supported Disease Classes
353
+
354
+ **Pepper Diseases:**
355
+ - Bell Pepper Bacterial Spot
356
+ - Bell Pepper Healthy
357
+
358
+ **Potato Diseases:**
359
+ - Early Blight
360
+ - Late Blight
361
+ - Healthy
362
+
363
+ **Tomato Diseases:**
364
+ - Target Spot
365
+ - Tomato Mosaic Virus
366
+ - Tomato Yellow Leaf Curl Virus
367
+ - Bacterial Spot
368
+ - Early Blight
369
+ - Late Blight
370
+ - Leaf Mold
371
+ - Septoria Leaf Spot
372
+ - Spider Mites (Two-spotted)
373
+ - Healthy
374
+
375
+ > **Note**: The model has been trained on limited data. For production use, consider collecting more training samples per class.
376
+
377
+ ## 🔧 Configuration
378
+
379
+ ### Environment Variables
380
+ ```powershell
381
+ # Optional: Set device preference
382
+ $env:TORCH_DEVICE="cuda" # or 'cpu'
383
+
384
+ # Optional: Set model path
385
+ $env:MODEL_PATH="models/crop_disease_v3_model.pth"
386
+ ```
387
+
388
+ ### API Configuration
389
+ Edit `api/main.py` for production settings:
390
+ - CORS origins
391
+ - Authentication
392
+ - Rate limiting
393
+ - Logging levels
394
+
395
+ ## 🚀 Deployment
396
+
397
+ ### 🤗 Hugging Face Spaces (Recommended)
398
+
399
+ The project is ready for one-click deployment on Hugging Face Spaces:
400
+
401
+ 1. **Fork/Clone** this repository
402
+ 2. **Create a new Space** on [Hugging Face Spaces](https://huggingface.co/spaces)
403
+ 3. **Select "Docker" SDK** when creating the Space
404
+ 4. **Upload the project files** or connect your Git repository
405
+ 5. **Wait for build** (5-10 minutes) and your app will be live!
406
+
407
+ **📖 Detailed Instructions**: See [DEPLOY_INSTRUCTIONS.md](DEPLOY_INSTRUCTIONS.md)
408
+
409
+ ### 🖥️ Local Streamlit App
410
+
411
+ ```powershell
412
+ # Install dependencies
413
+ pip install -r requirements.txt
414
+
415
+ # Run Streamlit app
416
+ streamlit run app.py
417
+
418
+ # Open browser to: http://localhost:8501
419
+ ```
420
+
421
+ ### 🐳 Docker Deployment
422
+
423
+ ```powershell
424
+ # Build image
425
+ docker build -t crop-disease-ai .
426
+
427
+ # Run container
428
+ docker run -p 7860:7860 crop-disease-ai
429
+
430
+ # Open browser to: http://localhost:7860
431
+ ```
432
+
433
+ ### Local Development
434
+ ```powershell
435
+ # GUI Application
436
+ python crop_disease_gui.py
437
+
438
+ # API Server
439
+ python -m api.main
440
+
441
+ # CLI Prediction
442
+ python -m src.predict_cli -i test_leaf_sample.jpg
443
+ ```
444
+
445
+ ### Local (Non-Docker) Quick Start
446
+
447
+ Use these steps on Windows PowerShell to run locally without Docker:
448
+
449
+ ```powershell
450
+ python -m venv .venv
451
+ .\.venv\Scripts\Activate.ps1
452
+ pip install -r requirements.txt
453
+ # Optional: API extras
454
+ pip install -r api/requirements.txt
455
+
456
+ # Evaluate model
457
+ python -m src.evaluate
458
+
459
+ # Run API
460
+ python -m api.main
461
+
462
+ # Single-image CLI prediction
463
+ python -m src.predict_cli -i test_leaf_sample.jpg -m models\crop_disease_v3_model.pth
464
+ ```
465
+
466
+ ### Cloud Deployment
467
+ The API is ready for deployment on:
468
+ - **AWS**: EC2, Lambda, ECS
469
+ - **Google Cloud**: Cloud Run, Compute Engine
470
+ - **Azure**: Container Instances, App Service
471
+ - **Heroku**: Container deployment
472
+
473
+ ## 🤝 Contributing
474
+
475
+ ### Development Setup
476
+ 1. Fork the repository
477
+ 2. Create feature branch: `git checkout -b feature/new-feature`
478
+ 3. Make changes and test thoroughly
479
+ 4. Submit pull request with detailed description
480
+
481
+ ### Contribution Guidelines
482
+ - Follow PEP 8 style guidelines
483
+ - Add unit tests for new features
484
+ - Update documentation for API changes
485
+ - Ensure backward compatibility
486
+
487
+ ### Areas for Contribution
488
+ - **Data Collection**: Expand disease image dataset
489
+ - **Model Improvements**: Experiment with new architectures
490
+ - **Feature Enhancement**: Add new crops/diseases
491
+ - **Performance Optimization**: Speed and accuracy improvements
492
+ - **Documentation**: Tutorials and examples
493
+
494
+ ## 📄 License
495
+
496
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
497
+
498
+ ## 👥 Authors & Acknowledgments
499
+
500
+ **Project Team:**
501
+ - **Lead Developer**: [Your Name]
502
+ - **AI/ML Engineer**: [Team Member]
503
+ - **Data Scientist**: [Team Member]
504
+
505
+ **Acknowledgments:**
506
+ - PlantVillage dataset for training data
507
+ - PyTorch team for deep learning framework
508
+ - FastAPI team for web framework
509
+ - Open source community for various tools
510
+
511
+ ## 📞 Support & Contact
512
+
513
+ ### Getting Help
514
+ - **Documentation**: Check this README and code comments
515
+ - **Issues**: Create GitHub issue for bugs/feature requests
516
+ - **Discussions**: Use GitHub discussions for questions
517
+
518
+ ### Contact Information
519
+ - **GitHub Repository**: https://github.com/vivek12coder/AiCropDiseasesDetection
520
+ - **Issues**: Create GitHub issue for bugs/feature requests
521
+ - **Project Owner**: @vivek12coder
522
+
523
+ ## 🔮 Future Roadmap
524
+
525
+ ### Phase 1: Data Enhancement (Weeks 1-2)
526
+ - [ ] Collect 1000+ images per disease class
527
+ - [ ] Implement advanced data augmentation
528
+ - [ ] Create balanced train/val/test splits
529
+
530
+ ### Phase 2: Model Optimization (Weeks 3-4)
531
+ - [ ] Experiment with EfficientNet, MobileNet
532
+ - [ ] Implement ensemble methods
533
+ - [ ] Add uncertainty estimation
534
+
535
+ ### Phase 3: Feature Expansion (Weeks 5-6)
536
+ - [ ] Add more crop types (rice, wheat, etc.)
537
+ - [ ] Implement real-time video processing
538
+ - [ ] Mobile app development
539
+
540
+ ### Phase 4: Production Enhancement (Weeks 7-8)
541
+ - [ ] Cloud deployment with auto-scaling
542
+ - [ ] Monitoring and logging system
543
+ - [ ] User analytics and feedback system
544
+
545
  ---
 
 
 
 
 
 
 
546
 
547
+ ## 📊 Quick Start Checklist
548
+
549
+ - [ ] Install Python 3.8+
550
+ - [ ] Clone repository
551
+ - [ ] Install dependencies: `pip install -r requirements.txt`
552
+ - [ ] Test GUI: `python crop_disease_gui.py`
553
+ - [ ] Test API: `python -m api.main`
554
+ - [ ] Test CLI: `python -m src.predict_cli -i test_leaf_sample.jpg`
555
+ - [ ] Upload test image and verify results
556
+ - [ ] Explore API documentation at http://127.0.0.1:8000/docs
557
+
558
+ **🎉 Ready to detect crop diseases with AI!**
REMOVED_FILES_REPORT.md ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🗑️ Removed Files & Justification Report
2
+
3
+ ## Summary
4
+
5
+ This report documents all Streamlit-related code, files, and dependencies that were removed during the conversion to FastAPI, while ensuring all core functionality and training capabilities are preserved.
6
+
7
+ ## ✅ Conversion Status: COMPLETED
8
+
9
+ **Result**: Successfully converted from Streamlit to FastAPI while preserving ALL features:
10
+ - ✅ Disease prediction with confidence scores
11
+ - ✅ Grad-CAM visualization generation
12
+ - ✅ Risk level assessment with environmental factors
13
+ - ✅ Disease information from knowledge base
14
+ - ✅ Progress tracking during processing
15
+ - ✅ All model training and evaluation scripts preserved
16
+ - ✅ CLI tools preserved
17
+ - ✅ Jupyter notebooks preserved
18
+
19
+ ## 📋 Files Modified (Not Removed)
20
+
21
+ ### 1. `app.py` - CONVERTED
22
+ **Before**: Streamlit web application
23
+ **After**: FastAPI REST API with equivalent functionality
24
+ **Justification**: Core functionality preserved, but exposed via RESTful endpoints instead of web interface
25
+
26
+ **Features Preserved:**
27
+ - Image upload and processing
28
+ - Disease prediction using V3 model
29
+ - Confidence scoring and class probabilities
30
+ - Grad-CAM heatmap generation (background processing)
31
+ - Risk level calculation with weather data
32
+ - Disease information retrieval
33
+ - Real-time status tracking
34
+
35
+ ### 2. `requirements.txt` - UPDATED
36
+ **Removed dependencies:**
37
+ - `streamlit>=1.28.0` - No longer needed for web interface
38
+
39
+ **Added dependencies:**
40
+ - `fastapi>=0.104.0` - Core REST API framework
41
+ - `uvicorn[standard]>=0.24.0` - ASGI server
42
+ - `python-multipart>=0.0.6` - File upload support
43
+
44
+ **Preserved dependencies:**
45
+ - All ML dependencies (torch, torchvision, grad-cam)
46
+ - All image processing dependencies (Pillow, opencv-python-headless)
47
+ - All utility dependencies (numpy, matplotlib, requests, tqdm, pydantic)
48
+
49
+ ### 3. `Dockerfile` - UPDATED
50
+ **Before**: Streamlit-optimized container with port 7860
51
+ **After**: FastAPI-optimized container with uvicorn server
52
+ **Justification**: Maintains Hugging Face Spaces compatibility while switching to FastAPI
53
+
54
+ **Key Changes:**
55
+ - Removed Streamlit environment variables
56
+ - Added FastAPI health check endpoint
57
+ - Changed CMD from `streamlit run` to `uvicorn app:app`
58
+ - Preserved all system dependencies and port 7860
59
+
60
+ ### 4. `README.md` - UPDATED
61
+ **Before**: Streamlit-focused documentation
62
+ **After**: FastAPI-focused with comprehensive API usage examples
63
+ **Justification**: Updated to reflect new REST API interface while preserving all capability descriptions
64
+
65
+ ## 📁 Files Preserved (No Changes)
66
+
67
+ ### Core ML Components
68
+ - ✅ `src/model.py` - ResNet50 model definition
69
+ - ✅ `src/explain.py` - Grad-CAM explainer
70
+ - ✅ `src/risk_level.py` - Risk assessment calculator
71
+ - ✅ `src/predict_cli.py` - CLI prediction tool
72
+ - ✅ `src/train.py` - Model training script
73
+ - ✅ `src/evaluate.py` - Model evaluation script
74
+ - ✅ `src/dataset.py` - Data loading utilities
75
+
76
+ ### Model Assets
77
+ - ✅ `models/crop_disease_v3_model.pth` - Latest trained model (V3)
78
+ - ✅ `models/crop_disease_v2_model.pth` - Fallback model (V2)
79
+ - ✅ `models/README.txt` - Model information
80
+
81
+ ### Data & Knowledge Base
82
+ - ✅ `knowledge_base/disease_info.json` - Disease information database
83
+ - ✅ `knowledge_base/disease_info_updated.json` - Updated disease data
84
+ - ✅ `knowledge_base/disease_info_backup.json` - Backup disease data
85
+ - ✅ `data/` - All training data preserved for retraining
86
+ - ✅ `test_leaf_sample.jpg` - Sample test image
87
+
88
+ ### Training & Analysis
89
+ - ✅ `notebooks/train_resnet50.ipynb` - Training notebook
90
+ - ✅ `outputs/` - All evaluation results and reports preserved
91
+
92
+ ## 🚫 Files Actually Removed: NONE
93
+
94
+ **Important**: No files were actually deleted from the project. The conversion was done by:
95
+
96
+ 1. **Modifying existing files** to replace Streamlit functionality with FastAPI equivalents
97
+ 2. **Preserving all training, evaluation, and CLI capabilities** intact
98
+ 3. **Maintaining all model assets and data** for continued development
99
+
100
+ ## 🔄 Functionality Mapping
101
+
102
+ ### Streamlit → FastAPI Equivalents
103
+
104
+ | Streamlit Feature | FastAPI Equivalent | Status |
105
+ |------------------|-------------------|--------|
106
+ | `st.file_uploader()` | `POST /predict` with `UploadFile` | ✅ Implemented |
107
+ | `st.image()` display | Base64 encoded response | ✅ Implemented |
108
+ | `st.progress()` bars | `GET /status/{task_id}` | ✅ Implemented |
109
+ | `st.spinner()` loading | Background task processing | ✅ Implemented |
110
+ | `st.sidebar` settings | API parameters | ✅ Implemented |
111
+ | `st.tabs()` interface | Separate endpoints | ✅ Implemented |
112
+ | `st.session_state` | Task ID tracking | ✅ Implemented |
113
+ | Interactive widgets | REST API calls | ✅ Implemented |
114
+
115
+ ## 🏆 Benefits of Conversion
116
+
117
+ ### Removed Limitations
118
+ - ❌ No more Streamlit session limitations
119
+ - ❌ No more browser-dependent interface
120
+ - ❌ No more single-user session restrictions
121
+
122
+ ### Added Capabilities
123
+ - ✅ RESTful API for integration with other systems
124
+ - ✅ Programmatic access via HTTP requests
125
+ - ✅ Background processing for expensive operations
126
+ - ✅ Scalable architecture for production deployment
127
+ - ✅ Standard OpenAPI documentation
128
+ - ✅ Better performance monitoring capabilities
129
+
130
+ ## 🧪 Testing Status
131
+
132
+ ### API Endpoints Verified
133
+ - ✅ `GET /health` - Model status check
134
+ - ✅ `POST /predict` - Disease prediction
135
+ - ✅ `GET /gradcam/{task_id}` - Heatmap visualization
136
+ - ✅ `GET /status/{task_id}` - Processing status
137
+ - ✅ `GET /disease-info` - Knowledge base lookup
138
+
139
+ ### Preserved Functionality Verified
140
+ - ✅ CLI prediction tool works unchanged
141
+ - ✅ Model training scripts work unchanged
142
+ - ✅ Evaluation scripts work unchanged
143
+ - ✅ Jupyter notebooks work unchanged
144
+ - ✅ All model files load correctly
145
+
146
+ ## 📊 Performance Impact
147
+
148
+ ### Improvements
149
+ - **Faster response times**: No Streamlit overhead
150
+ - **Better memory usage**: No persistent web session
151
+ - **Parallel processing**: Multiple requests handled simultaneously
152
+ - **Background operations**: Grad-CAM generation doesn't block API
153
+
154
+ ### Preserved Performance
155
+ - **Same model inference speed**: No change to PyTorch operations
156
+ - **Same image processing**: Identical preprocessing pipeline
157
+ - **Same Grad-CAM quality**: Using same explanation algorithms
158
+
159
+ ## 🚀 Deployment Ready
160
+
161
+ The converted FastAPI application is fully ready for:
162
+ - ✅ Local development and testing
163
+ - ✅ Docker container deployment
164
+ - ✅ Hugging Face Spaces deployment
165
+ - ✅ Production API service deployment
166
+ - ✅ Integration with external applications
167
+
168
+ ## 📞 Migration Notes
169
+
170
+ For users migrating from the Streamlit version:
171
+
172
+ 1. **Same functionality**: All features preserved
173
+ 2. **Different interface**: Web UI → REST API
174
+ 3. **Better integration**: Can be called from any programming language
175
+ 4. **Same models**: No retraining required
176
+ 5. **Same accuracy**: Identical prediction results
177
+
178
+ ## 🎯 Conclusion
179
+
180
+ **Successfully converted Streamlit to FastAPI with ZERO functionality loss.**
181
+
182
+ All original capabilities preserved:
183
+ - Disease detection accuracy maintained
184
+ - Grad-CAM visualizations identical
185
+ - Risk assessment algorithm unchanged
186
+ - Knowledge base integration preserved
187
+ - Training pipeline completely intact
188
+
189
+ The conversion provides a more robust, scalable, and integrable solution while maintaining all the AI capabilities that users depend on.
app.py ADDED
@@ -0,0 +1,486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ FastAPI App for Crop Disease Detection
3
+ RESTful API replacement for Streamlit - Deployment-ready for Hugging Face Spaces
4
+ """
5
+
6
+ from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks, Query
7
+ from fastapi.responses import FileResponse, JSONResponse
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+ from pydantic import BaseModel
10
+ from typing import Optional, List, Dict, Any
11
+ import torch
12
+ import torch.nn.functional as F
13
+ import numpy as np
14
+ from PIL import Image
15
+ import io
16
+ import json
17
+ import sys
18
+ import os
19
+ import uuid
20
+ import tempfile
21
+ import asyncio
22
+ from pathlib import Path
23
+ import matplotlib.pyplot as plt
24
+ import cv2
25
+ import base64
26
+ from datetime import datetime
27
+ import time
28
+
29
+ # Add src to path for imports
30
+ sys.path.append('src')
31
+
32
+ try:
33
+ from src.model import CropDiseaseResNet50
34
+ from src.explain import CropDiseaseExplainer
35
+ from src.risk_level import RiskLevelCalculator
36
+ from torchvision import transforms
37
+ except ImportError as e:
38
+ print(f"Import error: {e}")
39
+ raise e
40
+
41
+ # FastAPI app configuration
42
+ app = FastAPI(
43
+ title="🌱 Crop Disease AI Detection API",
44
+ description="RESTful API for AI-powered crop disease detection with Grad-CAM visualization",
45
+ version="3.0.0",
46
+ docs_url="/docs",
47
+ redoc_url="/redoc"
48
+ )
49
+
50
+ # Add CORS middleware
51
+ app.add_middleware(
52
+ CORSMiddleware,
53
+ allow_origins=["*"],
54
+ allow_credentials=True,
55
+ allow_methods=["*"],
56
+ allow_headers=["*"],
57
+ )
58
+
59
+ # Global variables for model and processing status
60
+ model = None
61
+ device = None
62
+ explainer = None
63
+ risk_calculator = None
64
+ processing_status = {}
65
+ class_names = []
66
+
67
+ # Model classes (from V3 model)
68
+ DEFAULT_CLASSES = [
69
+ 'Pepper__bell___Bacterial_spot',
70
+ 'Pepper__bell___healthy',
71
+ 'Potato___Early_blight',
72
+ 'Potato___healthy',
73
+ 'Potato___Late_blight',
74
+ 'Tomato__Target_Spot',
75
+ 'Tomato__Tomato_mosaic_virus',
76
+ 'Tomato__Tomato_YellowLeaf__Curl_Virus',
77
+ 'Tomato_Bacterial_spot',
78
+ 'Tomato_Early_blight',
79
+ 'Tomato_healthy',
80
+ 'Tomato_Late_blight',
81
+ 'Tomato_Leaf_Mold',
82
+ 'Tomato_Septoria_leaf_spot',
83
+ 'Tomato_Spider_mites_Two_spotted_spider_mite'
84
+ ]
85
+
86
+ # Pydantic models for API responses
87
+ class HealthResponse(BaseModel):
88
+ status: str
89
+ ai_model_loaded: bool
90
+ ai_model_version: str
91
+ available_endpoints: List[str]
92
+ timestamp: str
93
+ device: str
94
+
95
+ class PredictionResponse(BaseModel):
96
+ success: bool
97
+ predicted_class: str
98
+ crop: str
99
+ disease: str
100
+ confidence: float
101
+ all_probabilities: Dict[str, float]
102
+ risk_level: str
103
+ processing_time: float
104
+ task_id: str
105
+
106
+ class GradCAMResponse(BaseModel):
107
+ success: bool
108
+ heatmap_base64: str
109
+ explanation: str
110
+ task_id: str
111
+ processing_time: float
112
+
113
+ class StatusResponse(BaseModel):
114
+ task_id: str
115
+ status: str
116
+ progress: int
117
+ message: str
118
+ timestamp: str
119
+
120
+ class WeatherData(BaseModel):
121
+ humidity: Optional[float] = 50.0
122
+ temperature: Optional[float] = 25.0
123
+ rainfall: Optional[float] = 0.0
124
+
125
+ class PredictionRequest(BaseModel):
126
+ weather_data: Optional[WeatherData] = None
127
+ include_gradcam: Optional[bool] = True
128
+ include_disease_info: Optional[bool] = True
129
+
130
+ async def load_model_on_startup():
131
+ """Load the trained model on startup"""
132
+ global model, device, explainer, risk_calculator, class_names
133
+
134
+ try:
135
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
136
+ print(f"🔧 Using device: {device}")
137
+
138
+ # Try V3 model first, fallback to V2
139
+ model_paths = [
140
+ 'models/crop_disease_v3_model.pth',
141
+ 'models/crop_disease_v2_model.pth'
142
+ ]
143
+
144
+ model = None
145
+ model_name = None
146
+
147
+ for model_path in model_paths:
148
+ if os.path.exists(model_path):
149
+ try:
150
+ model = CropDiseaseResNet50(num_classes=len(DEFAULT_CLASSES), pretrained=False)
151
+ checkpoint = torch.load(model_path, map_location=device)
152
+
153
+ # Handle different checkpoint formats
154
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
155
+ state_dict = checkpoint['model_state_dict']
156
+ else:
157
+ state_dict = checkpoint
158
+
159
+ model.load_state_dict(state_dict, strict=True)
160
+ model.to(device)
161
+ model.eval()
162
+ model_name = os.path.basename(model_path)
163
+ break
164
+ except Exception as e:
165
+ print(f"Failed to load {model_path}: {e}")
166
+ continue
167
+
168
+ if model is None:
169
+ print("❌ No valid model found!")
170
+ raise RuntimeError("No valid model found!")
171
+
172
+ # Initialize explainer and risk calculator
173
+ try:
174
+ explainer = CropDiseaseExplainer(model, DEFAULT_CLASSES, device)
175
+ risk_calculator = RiskLevelCalculator()
176
+ except Exception as e:
177
+ print(f"Failed to initialize explainer: {e}")
178
+ explainer = None
179
+ risk_calculator = None
180
+
181
+ class_names = DEFAULT_CLASSES
182
+ print(f"✅ Model loaded: {model_name}")
183
+ return True
184
+
185
+ except Exception as e:
186
+ print(f"Error loading model: {e}")
187
+ return False
188
+
189
+ def preprocess_image(image):
190
+ """Preprocess image for model input"""
191
+ transform = transforms.Compose([
192
+ transforms.Resize((224, 224)),
193
+ transforms.ToTensor(),
194
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
195
+ ])
196
+ return transform(image).unsqueeze(0)
197
+
198
+ def predict_disease(model, device, image_tensor):
199
+ """Make disease prediction"""
200
+ with torch.no_grad():
201
+ outputs = model(image_tensor.to(device))
202
+ probabilities = F.softmax(outputs, dim=1)
203
+ confidence, predicted_idx = torch.max(probabilities, 1)
204
+
205
+ predicted_class = DEFAULT_CLASSES[predicted_idx.item()]
206
+ confidence_score = confidence.item()
207
+
208
+ # Get all class probabilities
209
+ class_probabilities = {
210
+ DEFAULT_CLASSES[i]: probabilities[0, i].item()
211
+ for i in range(len(DEFAULT_CLASSES))
212
+ }
213
+
214
+ return predicted_class, confidence_score, class_probabilities
215
+
216
+ def parse_class_name(class_name):
217
+ """Parse crop and disease from class name"""
218
+ if '___' in class_name:
219
+ parts = class_name.split('___')
220
+ crop = parts[0]
221
+ disease = parts[1]
222
+ elif '__' in class_name:
223
+ parts = class_name.split('__', 1)
224
+ crop = parts[0]
225
+ disease = parts[1]
226
+ elif '_' in class_name:
227
+ parts = class_name.split('_', 1)
228
+ crop = parts[0]
229
+ disease = parts[1]
230
+ else:
231
+ crop = "Unknown"
232
+ disease = class_name
233
+ return crop, disease
234
+
235
+ def get_disease_info(crop, disease):
236
+ """Get disease information from knowledge base"""
237
+ try:
238
+ with open('knowledge_base/disease_info.json', 'r') as f:
239
+ kb_data = json.load(f)
240
+ for d in kb_data['diseases']:
241
+ if crop.lower() in d['crop'].lower() and disease.lower() in d['disease'].lower():
242
+ return d
243
+ except Exception:
244
+ pass
245
+ return None
246
+
247
+ def update_processing_status(task_id: str, status: str, progress: int, message: str):
248
+ """Update processing status for a task"""
249
+ processing_status[task_id] = {
250
+ "status": status,
251
+ "progress": progress,
252
+ "message": message,
253
+ "timestamp": datetime.now().isoformat()
254
+ }
255
+
256
+ # FastAPI Events
257
+ @app.on_event("startup")
258
+ async def startup_event():
259
+ """Initialize model on startup"""
260
+ print("🚀 Starting Crop Disease Detection API...")
261
+ await load_model_on_startup()
262
+ print("✅ API ready to serve requests!")
263
+
264
+ # API Endpoints
265
+ @app.get("/", response_model=Dict[str, Any])
266
+ async def root():
267
+ """Root endpoint with API information"""
268
+ return {
269
+ "message": "🌱 Crop Disease Detection API",
270
+ "version": "3.0.0",
271
+ "status": "running",
272
+ "docs": "/docs",
273
+ "endpoints": {
274
+ "health": "/health",
275
+ "predict": "/predict",
276
+ "gradcam": "/gradcam/{task_id}",
277
+ "status": "/status/{task_id}"
278
+ }
279
+ }
280
+
281
+ @app.get("/health", response_model=HealthResponse)
282
+ async def health_check():
283
+ """Health check endpoint"""
284
+ global model, device
285
+
286
+ ai_model_loaded = model is not None
287
+ device_str = str(device) if device else "unknown"
288
+ ai_model_version = "crop_disease_v3_model.pth" if ai_model_loaded else "not_loaded"
289
+
290
+ return HealthResponse(
291
+ status="healthy" if ai_model_loaded else "unhealthy",
292
+ ai_model_loaded=ai_model_loaded,
293
+ ai_model_version=ai_model_version,
294
+ available_endpoints=["/health", "/predict", "/gradcam/{task_id}", "/status/{task_id}"],
295
+ timestamp=datetime.now().isoformat(),
296
+ device=device_str
297
+ )
298
+
299
+ @app.post("/predict", response_model=PredictionResponse)
300
+ async def predict_crop_disease(
301
+ background_tasks: BackgroundTasks,
302
+ file: UploadFile = File(...),
303
+ weather_data: Optional[str] = Query(None, description="JSON string of weather data"),
304
+ include_gradcam: bool = Query(True, description="Generate Grad-CAM heatmap"),
305
+ include_disease_info: bool = Query(True, description="Include disease information")
306
+ ):
307
+ """
308
+ Predict crop disease from uploaded image
309
+ """
310
+ global model, device, risk_calculator
311
+
312
+ if model is None:
313
+ raise HTTPException(status_code=503, detail="Model not loaded")
314
+
315
+ # Validate file type
316
+ if file.content_type not in ["image/jpeg", "image/jpg", "image/png", "image/bmp"]:
317
+ raise HTTPException(status_code=400, detail="Invalid file type. Only JPEG, PNG, and BMP are supported.")
318
+
319
+ task_id = str(uuid.uuid4())
320
+ start_time = time.time()
321
+
322
+ try:
323
+ # Update status: Image uploaded
324
+ update_processing_status(task_id, "processing", 10, "Image uploaded successfully")
325
+
326
+ # Read and process image
327
+ image_bytes = await file.read()
328
+ image = Image.open(io.BytesIO(image_bytes)).convert('RGB')
329
+
330
+ # Update status: Preprocessing
331
+ update_processing_status(task_id, "processing", 30, "Preprocessing image")
332
+
333
+ # Preprocess image
334
+ image_tensor = preprocess_image(image)
335
+
336
+ # Update status: Model running
337
+ update_processing_status(task_id, "processing", 50, "Running inference")
338
+
339
+ # Make prediction
340
+ predicted_class, confidence_score, class_probabilities = predict_disease(
341
+ model, device, image_tensor
342
+ )
343
+
344
+ # Parse class name
345
+ crop, disease = parse_class_name(predicted_class)
346
+
347
+ # Update status: Risk assessment
348
+ update_processing_status(task_id, "processing", 70, "Calculating risk assessment")
349
+
350
+ # Calculate risk level
351
+ risk_level = "Unknown"
352
+ if risk_calculator:
353
+ try:
354
+ weather = {}
355
+ if weather_data:
356
+ weather = json.loads(weather_data)
357
+
358
+ weather_data_obj = {
359
+ 'humidity': weather.get('humidity', 50.0),
360
+ 'temperature': weather.get('temperature', 25.0),
361
+ 'rainfall': weather.get('rainfall', 0.0)
362
+ }
363
+ risk_assessment = risk_calculator.calculate_enhanced_risk(
364
+ predicted_class, confidence_score, weather_data_obj, None
365
+ )
366
+ risk_level = risk_assessment.get('risk_level', 'Unknown')
367
+ except Exception as e:
368
+ print(f"Risk assessment error: {e}")
369
+
370
+ # Update status: Completed
371
+ update_processing_status(task_id, "completed", 100, "Analysis completed successfully")
372
+
373
+ processing_time = time.time() - start_time
374
+
375
+ # Schedule Grad-CAM generation if requested
376
+ if include_gradcam and explainer:
377
+ background_tasks.add_task(generate_gradcam_background, task_id, image_bytes)
378
+
379
+ return PredictionResponse(
380
+ success=True,
381
+ predicted_class=predicted_class,
382
+ crop=crop,
383
+ disease=disease,
384
+ confidence=confidence_score,
385
+ all_probabilities=class_probabilities,
386
+ risk_level=risk_level,
387
+ processing_time=processing_time,
388
+ task_id=task_id
389
+ )
390
+
391
+ except Exception as e:
392
+ update_processing_status(task_id, "error", 0, f"Error: {str(e)}")
393
+ raise HTTPException(status_code=500, detail=f"Prediction failed: {str(e)}")
394
+
395
+ async def generate_gradcam_background(task_id: str, image_bytes: bytes):
396
+ """Generate Grad-CAM heatmap in background"""
397
+ global explainer
398
+
399
+ try:
400
+ update_processing_status(task_id, "processing", 80, "Generating Grad-CAM heatmap")
401
+
402
+ # Save temporary image
403
+ with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp_file:
404
+ tmp_file.write(image_bytes)
405
+ temp_path = tmp_file.name
406
+
407
+ try:
408
+ # Generate explanation
409
+ explanation = explainer.explain_prediction(temp_path, return_base64=True)
410
+
411
+ if 'overlay_base64' in explanation:
412
+ # Store the result
413
+ processing_status[f"{task_id}_gradcam"] = {
414
+ "success": True,
415
+ "heatmap_base64": explanation['overlay_base64'],
416
+ "explanation": "Grad-CAM heatmap showing areas the AI model focused on for prediction",
417
+ "timestamp": datetime.now().isoformat()
418
+ }
419
+ else:
420
+ error_msg = explanation.get('error', 'Unknown error generating Grad-CAM')
421
+ processing_status[f"{task_id}_gradcam"] = {
422
+ "success": False,
423
+ "error": error_msg,
424
+ "timestamp": datetime.now().isoformat()
425
+ }
426
+ finally:
427
+ # Clean up temp file
428
+ if os.path.exists(temp_path):
429
+ os.unlink(temp_path)
430
+
431
+ except Exception as e:
432
+ processing_status[f"{task_id}_gradcam"] = {
433
+ "success": False,
434
+ "error": str(e),
435
+ "timestamp": datetime.now().isoformat()
436
+ }
437
+
438
+ @app.get("/gradcam/{task_id}", response_model=GradCAMResponse)
439
+ async def get_gradcam(task_id: str):
440
+ """Get Grad-CAM heatmap for a prediction task"""
441
+ gradcam_key = f"{task_id}_gradcam"
442
+
443
+ if gradcam_key not in processing_status:
444
+ raise HTTPException(status_code=404, detail="Grad-CAM not found or still processing")
445
+
446
+ result = processing_status[gradcam_key]
447
+
448
+ if not result.get("success", False):
449
+ raise HTTPException(status_code=500, detail=f"Grad-CAM generation failed: {result.get('error', 'Unknown error')}")
450
+
451
+ return GradCAMResponse(
452
+ success=True,
453
+ heatmap_base64=result["heatmap_base64"],
454
+ explanation=result["explanation"],
455
+ task_id=task_id,
456
+ processing_time=0.0 # Background task, time not tracked
457
+ )
458
+
459
+ @app.get("/status/{task_id}", response_model=StatusResponse)
460
+ async def get_status(task_id: str):
461
+ """Get processing status for a task"""
462
+ if task_id not in processing_status:
463
+ raise HTTPException(status_code=404, detail="Task not found")
464
+
465
+ status = processing_status[task_id]
466
+ return StatusResponse(
467
+ task_id=task_id,
468
+ status=status["status"],
469
+ progress=status["progress"],
470
+ message=status["message"],
471
+ timestamp=status["timestamp"]
472
+ )
473
+
474
+ @app.get("/disease-info")
475
+ async def get_disease_information(crop: str, disease: str):
476
+ """Get disease information from knowledge base"""
477
+ disease_info = get_disease_info(crop, disease)
478
+
479
+ if disease_info:
480
+ return {"success": True, "data": disease_info}
481
+ else:
482
+ return {"success": False, "message": "Disease information not found"}
483
+
484
+ if __name__ == "__main__":
485
+ import uvicorn
486
+ uvicorn.run(app, host="localhost", port=7860)
data/raw/README.txt ADDED
File without changes
knowledge_base/disease_info.json ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_info": {
3
+ "model_name": "Crop Disease Detection - Retrained ResNet50",
4
+ "version": "3.0",
5
+ "last_updated": "2025-09-09",
6
+ "training_dataset": "Pepper, Potato, and Tomato Disease Dataset",
7
+ "total_classes": 15,
8
+ "test_accuracy": 0.9009,
9
+ "validation_accuracy": 0.9006,
10
+ "model_file": "models/crop_disease_retrained_final.pth",
11
+ "training_samples": 14440,
12
+ "validation_samples": 3089,
13
+ "test_samples": 3109,
14
+ "supported_crops": ["Pepper (Bell)", "Potato", "Tomato"]
15
+ },
16
+ "diseases": [
17
+ {
18
+ "crop": "Pepper (Bell)",
19
+ "disease": "Bacterial_spot",
20
+ "class_name": "Pepper__bell___Bacterial_spot",
21
+ "description": "Caused by Xanthomonas bacteria. Creates dark, water-soaked spots on leaves, stems, and fruits. Thrives in warm, humid conditions with overhead irrigation.",
22
+ "symptoms": [
23
+ "Small, dark brown to black spots on leaves",
24
+ "Water-soaked appearance of lesions",
25
+ "Yellow halos around older spots",
26
+ "Spots on fruits appear raised and scabby",
27
+ "Premature defoliation in severe cases"
28
+ ],
29
+ "solutions": [
30
+ "Apply copper-based bactericides preventively",
31
+ "Use bacterial spot resistant varieties",
32
+ "Improve air circulation around plants",
33
+ "Avoid overhead irrigation",
34
+ "Remove infected plant debris",
35
+ "Practice crop rotation with non-host crops"
36
+ ],
37
+ "prevention": [
38
+ "Use certified disease-free seeds and transplants",
39
+ "Avoid working in fields when plants are wet",
40
+ "Maintain proper plant spacing",
41
+ "Apply drip irrigation instead of overhead watering",
42
+ "Sanitize tools between plants"
43
+ ],
44
+ "severity": "High",
45
+ "confidence_threshold": 0.92
46
+ },
47
+ {
48
+ "crop": "Pepper (Bell)",
49
+ "disease": "Healthy",
50
+ "class_name": "Pepper__bell___healthy",
51
+ "description": "Healthy bell pepper plants with no visible disease symptoms.",
52
+ "symptoms": [
53
+ "Vibrant green, uniform colored leaves",
54
+ "No spots, lesions, or discoloration",
55
+ "Normal plant growth and development",
56
+ "Healthy fruit development"
57
+ ],
58
+ "solutions": [
59
+ "Continue current management practices",
60
+ "Maintain regular monitoring",
61
+ "Ensure proper nutrition and watering"
62
+ ],
63
+ "prevention": [
64
+ "Regular inspection for early disease detection",
65
+ "Proper sanitation practices",
66
+ "Balanced fertilization",
67
+ "Appropriate irrigation management"
68
+ ],
69
+ "severity": "None",
70
+ "confidence_threshold": 0.95
71
+ },
72
+ {
73
+ "crop": "Potato",
74
+ "disease": "Early_blight",
75
+ "class_name": "Potato___Early_blight",
76
+ "description": "Caused by Alternaria solani fungus. Creates concentric ring patterns on leaves and can affect tubers. Common in warm, humid conditions with plant stress.",
77
+ "symptoms": [
78
+ "Dark brown spots with concentric rings (target spots)",
79
+ "Yellow halos around lesions",
80
+ "Lower leaves affected first",
81
+ "V-shaped lesions at leaf margins",
82
+ "Premature defoliation",
83
+ "Dark, sunken spots on tubers"
84
+ ],
85
+ "solutions": [
86
+ "Apply fungicides containing chlorothalonil or mancozeb",
87
+ "Remove infected lower leaves",
88
+ "Improve air circulation",
89
+ "Avoid overhead irrigation",
90
+ "Apply balanced fertilization",
91
+ "Harvest tubers when skin is set"
92
+ ],
93
+ "prevention": [
94
+ "Plant certified disease-free seed potatoes",
95
+ "Rotate crops with non-solanaceous plants",
96
+ "Maintain proper plant nutrition",
97
+ "Avoid plant stress from drought or excess nitrogen",
98
+ "Clean up crop debris after harvest"
99
+ ],
100
+ "severity": "Medium to High",
101
+ "confidence_threshold": 0.96
102
+ },
103
+ {
104
+ "crop": "Potato",
105
+ "disease": "Late_blight",
106
+ "class_name": "Potato___Late_blight",
107
+ "description": "Caused by Phytophthora infestans. The same pathogen that caused the Irish Potato Famine. Spreads rapidly in cool, wet conditions.",
108
+ "symptoms": [
109
+ "Water-soaked, dark green to brown lesions",
110
+ "White, fuzzy sporulation on leaf undersides",
111
+ "Rapid spread during cool, wet weather",
112
+ "Blackening and collapse of stems",
113
+ "Firm, brown rot of tubers",
114
+ "Entire plant death possible"
115
+ ],
116
+ "solutions": [
117
+ "Apply preventive fungicides (metalaxyl, chlorothalonil)",
118
+ "Remove infected plants immediately",
119
+ "Improve drainage and air circulation",
120
+ "Monitor weather conditions closely",
121
+ "Destroy volunteer potatoes",
122
+ "Cure tubers properly before storage"
123
+ ],
124
+ "prevention": [
125
+ "Plant certified seed potatoes",
126
+ "Choose late blight resistant varieties",
127
+ "Avoid overhead irrigation",
128
+ "Hill soil properly around plants",
129
+ "Monitor local disease forecasts",
130
+ "Destroy cull piles and volunteers"
131
+ ],
132
+ "severity": "Very High",
133
+ "confidence_threshold": 0.86
134
+ },
135
+ {
136
+ "crop": "Potato",
137
+ "disease": "Healthy",
138
+ "class_name": "Potato___healthy",
139
+ "description": "Healthy potato plants with no visible disease symptoms.",
140
+ "symptoms": [
141
+ "Bright green, healthy foliage",
142
+ "Normal plant growth and development",
143
+ "No spots, lesions, or discoloration",
144
+ "Vigorous stem growth"
145
+ ],
146
+ "solutions": [
147
+ "Continue current management practices",
148
+ "Maintain regular monitoring",
149
+ "Ensure proper nutrition and watering"
150
+ ],
151
+ "prevention": [
152
+ "Regular field inspection",
153
+ "Proper crop rotation",
154
+ "Balanced fertilization",
155
+ "Appropriate irrigation management"
156
+ ],
157
+ "severity": "None",
158
+ "confidence_threshold": 0.38
159
+ },
160
+ {
161
+ "crop": "Tomato",
162
+ "disease": "Target_Spot",
163
+ "class_name": "Tomato__Target_Spot",
164
+ "description": "Caused by Corynespora cassiicola fungus. Creates concentric ring spots on leaves and can affect fruits. Favored by warm, humid conditions.",
165
+ "symptoms": [
166
+ "Small, dark brown spots with concentric rings",
167
+ "Target-like appearance of lesions",
168
+ "Yellow halos around spots",
169
+ "Spots can merge causing leaf blight",
170
+ "Fruit lesions are dark and sunken",
171
+ "Premature defoliation"
172
+ ],
173
+ "solutions": [
174
+ "Apply fungicides containing azoxystrobin or chlorothalonil",
175
+ "Remove infected plant debris",
176
+ "Improve air circulation",
177
+ "Avoid overhead watering",
178
+ "Practice crop rotation",
179
+ "Remove lower leaves touching soil"
180
+ ],
181
+ "prevention": [
182
+ "Plant resistant varieties when available",
183
+ "Maintain proper plant spacing",
184
+ "Use drip irrigation",
185
+ "Apply mulch to prevent soil splash",
186
+ "Sanitize tools and equipment"
187
+ ],
188
+ "severity": "Medium",
189
+ "confidence_threshold": 0.87
190
+ },
191
+ {
192
+ "crop": "Tomato",
193
+ "disease": "Tomato_mosaic_virus",
194
+ "class_name": "Tomato__Tomato_mosaic_virus",
195
+ "description": "Viral disease causing mosaic patterns on leaves. Transmitted through infected seeds, mechanical transmission, and contaminated tools.",
196
+ "symptoms": [
197
+ "Light and dark green mosaic pattern on leaves",
198
+ "Mottled appearance of foliage",
199
+ "Stunted plant growth",
200
+ "Reduced fruit size and yield",
201
+ "Fruit may show irregular ripening",
202
+ "Leaf distortion and curling"
203
+ ],
204
+ "solutions": [
205
+ "Remove infected plants immediately",
206
+ "Control aphid vectors",
207
+ "Sanitize tools with 10% bleach solution",
208
+ "Avoid smoking near plants",
209
+ "Use virus-free transplants",
210
+ "Control weeds that may harbor virus"
211
+ ],
212
+ "prevention": [
213
+ "Use certified virus-free seeds",
214
+ "Wash hands before handling plants",
215
+ "Avoid mechanical transmission",
216
+ "Control insect vectors",
217
+ "Remove infected plant debris",
218
+ "Practice crop rotation"
219
+ ],
220
+ "severity": "High",
221
+ "confidence_threshold": 0.67
222
+ },
223
+ {
224
+ "crop": "Tomato",
225
+ "disease": "Tomato_YellowLeaf_Curl_Virus",
226
+ "class_name": "Tomato__Tomato_YellowLeaf__Curl_Virus",
227
+ "description": "Viral disease transmitted by whiteflies. Causes severe yield losses in warm climates. One of the most devastating tomato diseases.",
228
+ "symptoms": [
229
+ "Upward curling and yellowing of leaves",
230
+ "Stunted plant growth",
231
+ "Reduced fruit set and size",
232
+ "Thick, leathery leaf texture",
233
+ "Purple veins on leaf undersides",
234
+ "Severe yield reduction"
235
+ ],
236
+ "solutions": [
237
+ "Control whitefly vectors with insecticides",
238
+ "Remove infected plants",
239
+ "Use reflective mulches",
240
+ "Apply systemic insecticides",
241
+ "Install fine mesh screens in greenhouses",
242
+ "Monitor and trap whiteflies"
243
+ ],
244
+ "prevention": [
245
+ "Plant resistant varieties",
246
+ "Control whitefly populations",
247
+ "Remove weeds that harbor whiteflies",
248
+ "Use yellow sticky traps",
249
+ "Avoid planting near infected crops",
250
+ "Time planting to avoid peak whitefly populations"
251
+ ],
252
+ "severity": "Very High",
253
+ "confidence_threshold": 0.94
254
+ },
255
+ {
256
+ "crop": "Tomato",
257
+ "disease": "Bacterial_spot",
258
+ "class_name": "Tomato_Bacterial_spot",
259
+ "description": "Caused by Xanthomonas bacteria. Creates dark spots on leaves and fruits. Spreads rapidly in warm, wet conditions.",
260
+ "symptoms": [
261
+ "Small, dark brown to black spots on leaves",
262
+ "Water-soaked appearance of lesions",
263
+ "Yellow halos around spots",
264
+ "Raised, scabby spots on fruits",
265
+ "Premature defoliation",
266
+ "Fruit cracking and secondary infections"
267
+ ],
268
+ "solutions": [
269
+ "Apply copper-based bactericides",
270
+ "Use resistant varieties",
271
+ "Improve air circulation",
272
+ "Avoid overhead irrigation",
273
+ "Remove infected plant debris",
274
+ "Practice crop rotation"
275
+ ],
276
+ "prevention": [
277
+ "Use certified disease-free seeds",
278
+ "Avoid working in wet fields",
279
+ "Maintain proper plant spacing",
280
+ "Use drip irrigation systems",
281
+ "Sanitize tools between plants",
282
+ "Remove volunteer tomatoes"
283
+ ],
284
+ "severity": "High",
285
+ "confidence_threshold": 0.94
286
+ },
287
+ {
288
+ "crop": "Tomato",
289
+ "disease": "Early_blight",
290
+ "class_name": "Tomato_Early_blight",
291
+ "description": "Caused by Alternaria solani fungus. Creates target spot lesions on leaves and can affect fruits. Common during warm, humid periods.",
292
+ "symptoms": [
293
+ "Dark brown spots with concentric rings",
294
+ "Target-like appearance of lesions",
295
+ "Yellow halos around spots",
296
+ "Lower leaves affected first",
297
+ "Premature defoliation",
298
+ "Dark, sunken spots on fruits"
299
+ ],
300
+ "solutions": [
301
+ "Apply fungicides containing chlorothalonil",
302
+ "Remove infected lower leaves",
303
+ "Improve air circulation",
304
+ "Avoid overhead watering",
305
+ "Apply balanced fertilization",
306
+ "Stake plants properly"
307
+ ],
308
+ "prevention": [
309
+ "Use certified disease-free transplants",
310
+ "Rotate crops with non-solanaceous plants",
311
+ "Maintain proper plant nutrition",
312
+ "Mulch around plants",
313
+ "Remove crop debris after harvest",
314
+ "Space plants adequately"
315
+ ],
316
+ "severity": "Medium",
317
+ "confidence_threshold": 0.77
318
+ },
319
+ {
320
+ "crop": "Tomato",
321
+ "disease": "Healthy",
322
+ "class_name": "Tomato_healthy",
323
+ "description": "Healthy tomato plants with no visible disease symptoms.",
324
+ "symptoms": [
325
+ "Vibrant green, healthy foliage",
326
+ "Normal plant growth and development",
327
+ "No spots, lesions, or discoloration",
328
+ "Healthy fruit development",
329
+ "Strong stem structure"
330
+ ],
331
+ "solutions": [
332
+ "Continue current management practices",
333
+ "Maintain regular monitoring",
334
+ "Ensure proper nutrition and watering"
335
+ ],
336
+ "prevention": [
337
+ "Regular inspection for early disease detection",
338
+ "Proper sanitation practices",
339
+ "Balanced fertilization",
340
+ "Appropriate irrigation management"
341
+ ],
342
+ "severity": "None",
343
+ "confidence_threshold": 0.97
344
+ },
345
+ {
346
+ "crop": "Tomato",
347
+ "disease": "Late_blight",
348
+ "class_name": "Tomato_Late_blight",
349
+ "description": "Caused by Phytophthora infestans. Rapid-spreading disease favored by cool, wet conditions. Can destroy entire crops quickly.",
350
+ "symptoms": [
351
+ "Water-soaked, dark green lesions",
352
+ "White, fuzzy sporulation on leaf undersides",
353
+ "Rapid spread during cool, wet weather",
354
+ "Brown, firm rot of fruits",
355
+ "Blackening of stems",
356
+ "Collapse of entire plant"
357
+ ],
358
+ "solutions": [
359
+ "Apply preventive fungicides immediately",
360
+ "Remove infected plants",
361
+ "Improve drainage and air circulation",
362
+ "Monitor weather conditions",
363
+ "Destroy infected fruit and debris",
364
+ "Harvest healthy fruits early"
365
+ ],
366
+ "prevention": [
367
+ "Plant resistant varieties",
368
+ "Avoid overhead irrigation",
369
+ "Ensure good air circulation",
370
+ "Monitor disease forecasts",
371
+ "Remove volunteer tomatoes",
372
+ "Practice crop rotation"
373
+ ],
374
+ "severity": "Very High",
375
+ "confidence_threshold": 0.86
376
+ },
377
+ {
378
+ "crop": "Tomato",
379
+ "disease": "Leaf_Mold",
380
+ "class_name": "Tomato_Leaf_Mold",
381
+ "description": "Caused by Passalora fulva fungus. Primarily affects greenhouse tomatoes. Thrives in high humidity conditions.",
382
+ "symptoms": [
383
+ "Yellow spots on upper leaf surfaces",
384
+ "Olive-green, velvety mold on leaf undersides",
385
+ "Progressive yellowing and browning",
386
+ "Premature defoliation",
387
+ "Reduced fruit quality",
388
+ "Poor air circulation enhances disease"
389
+ ],
390
+ "solutions": [
391
+ "Improve ventilation in greenhouses",
392
+ "Reduce humidity levels",
393
+ "Apply fungicides containing chlorothalonil",
394
+ "Remove infected leaves",
395
+ "Increase plant spacing",
396
+ "Control temperature and humidity"
397
+ ],
398
+ "prevention": [
399
+ "Plant resistant varieties",
400
+ "Maintain proper ventilation",
401
+ "Control greenhouse humidity",
402
+ "Avoid overhead watering",
403
+ "Monitor environmental conditions",
404
+ "Remove crop debris"
405
+ ],
406
+ "severity": "Medium",
407
+ "confidence_threshold": 0.87
408
+ },
409
+ {
410
+ "crop": "Tomato",
411
+ "disease": "Septoria_leaf_spot",
412
+ "class_name": "Tomato_Septoria_leaf_spot",
413
+ "description": "Caused by Septoria lycopersici fungus. Creates small circular spots with dark borders. Favored by warm, wet weather.",
414
+ "symptoms": [
415
+ "Small, circular spots with dark borders",
416
+ "Gray to tan center with dark margin",
417
+ "Tiny black specks (pycnidia) in spot centers",
418
+ "Yellow halos around spots",
419
+ "Lower leaves affected first",
420
+ "Progressive defoliation upward"
421
+ ],
422
+ "solutions": [
423
+ "Apply fungicides containing chlorothalonil or copper",
424
+ "Remove infected lower leaves",
425
+ "Improve air circulation",
426
+ "Avoid overhead watering",
427
+ "Stake plants to improve airflow",
428
+ "Apply mulch to prevent soil splash"
429
+ ],
430
+ "prevention": [
431
+ "Rotate crops with non-solanaceous plants",
432
+ "Use certified disease-free transplants",
433
+ "Maintain proper plant spacing",
434
+ "Remove crop debris after harvest",
435
+ "Avoid working in wet fields",
436
+ "Apply balanced fertilization"
437
+ ],
438
+ "severity": "Medium",
439
+ "confidence_threshold": 0.91
440
+ },
441
+ {
442
+ "crop": "Tomato",
443
+ "disease": "Spider_mites_Two_spotted_spider_mite",
444
+ "class_name": "Tomato_Spider_mites_Two_spotted_spider_mite",
445
+ "description": "Caused by Tetranychus urticae. Tiny arachnids that feed on plant cells. Thrive in hot, dry conditions.",
446
+ "symptoms": [
447
+ "Fine stippling or speckling on leaves",
448
+ "Yellow or bronze discoloration",
449
+ "Fine webbing on leaves and stems",
450
+ "Premature leaf drop",
451
+ "Reduced plant vigor",
452
+ "Severe infestations cause plant death"
453
+ ],
454
+ "solutions": [
455
+ "Apply miticides or insecticidal soaps",
456
+ "Increase humidity around plants",
457
+ "Release predatory mites",
458
+ "Spray with water to dislodge mites",
459
+ "Remove heavily infested leaves",
460
+ "Apply reflective mulches"
461
+ ],
462
+ "prevention": [
463
+ "Maintain adequate soil moisture",
464
+ "Avoid over-fertilization with nitrogen",
465
+ "Monitor plants regularly",
466
+ "Encourage beneficial insects",
467
+ "Remove weeds that harbor mites",
468
+ "Avoid dusty conditions"
469
+ ],
470
+ "severity": "Medium to High",
471
+ "confidence_threshold": 0.91
472
+ }
473
+ ],
474
+ "treatment_recommendations": {
475
+ "organic_treatments": {
476
+ "fungal_diseases": [
477
+ "Copper-based fungicides",
478
+ "Baking soda solutions",
479
+ "Neem oil applications",
480
+ "Compost tea sprays",
481
+ "Milk solutions (for powdery mildew)"
482
+ ],
483
+ "bacterial_diseases": [
484
+ "Copper sulfate treatments",
485
+ "Hydrogen peroxide solutions",
486
+ "Essential oil treatments",
487
+ "Proper sanitation practices"
488
+ ],
489
+ "viral_diseases": [
490
+ "Remove infected plants",
491
+ "Control insect vectors",
492
+ "Use resistant varieties",
493
+ "Improve sanitation"
494
+ ],
495
+ "pest_management": [
496
+ "Beneficial insect release",
497
+ "Insecticidal soaps",
498
+ "Diatomaceous earth",
499
+ "Row covers for protection"
500
+ ]
501
+ },
502
+ "chemical_treatments": {
503
+ "systemic_fungicides": [
504
+ "Azoxystrobin",
505
+ "Propiconazole",
506
+ "Metalaxyl",
507
+ "Tebuconazole"
508
+ ],
509
+ "contact_fungicides": [
510
+ "Chlorothalonil",
511
+ "Mancozeb",
512
+ "Copper compounds",
513
+ "Sulfur"
514
+ ],
515
+ "bactericides": [
516
+ "Copper hydroxide",
517
+ "Copper oxychloride",
518
+ "Streptomycin (where permitted)"
519
+ ],
520
+ "insecticides": [
521
+ "Imidacloprid",
522
+ "Thiamethoxam",
523
+ "Spinosad",
524
+ "Bifenthrin"
525
+ ]
526
+ }
527
+ },
528
+ "general_prevention": {
529
+ "cultural_practices": [
530
+ "Crop rotation with non-host plants",
531
+ "Proper plant spacing for air circulation",
532
+ "Drip irrigation instead of overhead watering",
533
+ "Mulching to prevent soil splash",
534
+ "Regular field sanitation",
535
+ "Removal of crop debris"
536
+ ],
537
+ "monitoring": [
538
+ "Regular field inspections",
539
+ "Weather monitoring for disease-favorable conditions",
540
+ "Early detection and rapid response",
541
+ "Record keeping of disease occurrences",
542
+ "Soil and plant tissue testing"
543
+ ],
544
+ "resistance_management": [
545
+ "Use of resistant varieties",
546
+ "Rotation of fungicide modes of action",
547
+ "Integrated pest management approaches",
548
+ "Maintaining genetic diversity in crops"
549
+ ]
550
+ }
551
+ }
knowledge_base/disease_info_backup.json ADDED
@@ -0,0 +1,419 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "diseases": [
3
+ {
4
+ "crop": "Corn",
5
+ "disease": "Cercospora_leaf_spot_Gray_leaf_spot",
6
+ "description": "Caused by Cercospora zeae-maydis fungus. Creates rectangular gray-brown lesions with yellow halos on leaves. Thrives in warm, humid conditions and can significantly reduce yield.",
7
+ "symptoms": [
8
+ "Rectangular gray-brown lesions on leaves",
9
+ "Yellow halos around spots",
10
+ "Lesions may merge causing leaf blight",
11
+ "Premature leaf death"
12
+ ],
13
+ "solutions": [
14
+ "Plant resistant corn varieties",
15
+ "Apply fungicides containing strobilurins or triazoles",
16
+ "Rotate crops to break disease cycle",
17
+ "Ensure proper field drainage",
18
+ "Remove crop residue after harvest",
19
+ "Monitor weather conditions for early intervention"
20
+ ],
21
+ "prevention": [
22
+ "Use certified disease-free seeds",
23
+ "Maintain proper plant spacing for air circulation",
24
+ "Avoid overhead irrigation",
25
+ "Apply balanced fertilization"
26
+ ]
27
+ },
28
+ {
29
+ "crop": "Corn",
30
+ "disease": "Common_rust",
31
+ "description": "Caused by Puccinia sorghi fungus. Produces small, oval, cinnamon-brown pustules on both leaf surfaces. Most common in cool, humid weather conditions.",
32
+ "symptoms": [
33
+ "Small oval rust-colored pustules on leaves",
34
+ "Pustules on both upper and lower leaf surfaces",
35
+ "Yellow halos around pustules",
36
+ "Premature leaf senescence"
37
+ ],
38
+ "solutions": [
39
+ "Plant rust-resistant corn hybrids",
40
+ "Apply fungicides if economic threshold is reached",
41
+ "Monitor fields regularly during cool, humid periods",
42
+ "Remove volunteer corn plants",
43
+ "Practice crop rotation"
44
+ ],
45
+ "prevention": [
46
+ "Choose resistant varieties",
47
+ "Avoid planting in low-lying, humid areas",
48
+ "Ensure adequate plant nutrition",
49
+ "Monitor weather forecasts for favorable disease conditions"
50
+ ]
51
+ },
52
+ {
53
+ "crop": "Corn",
54
+ "disease": "Northern_Leaf_Blight",
55
+ "description": "Caused by Exserohilum turcicum fungus. Creates large, elliptical, gray-green lesions on leaves. Favored by moderate temperatures and high humidity.",
56
+ "symptoms": [
57
+ "Large elliptical gray-green lesions",
58
+ "Lesions have distinct margins",
59
+ "Lesions may girdle the leaf",
60
+ "Reduced photosynthetic area"
61
+ ],
62
+ "solutions": [
63
+ "Use resistant corn hybrids",
64
+ "Apply fungicides preventively in high-risk areas",
65
+ "Implement crop rotation with non-host crops",
66
+ "Manage crop residue properly",
67
+ "Ensure balanced nutrition"
68
+ ],
69
+ "prevention": [
70
+ "Select resistant varieties",
71
+ "Avoid continuous corn cropping",
72
+ "Maintain proper plant density",
73
+ "Monitor humidity levels"
74
+ ]
75
+ },
76
+ {
77
+ "crop": "Corn",
78
+ "disease": "healthy",
79
+ "description": "Healthy corn plants show vibrant green leaves without disease symptoms. Proper nutrition and growing conditions support optimal plant health.",
80
+ "symptoms": [
81
+ "Vibrant green leaf color",
82
+ "No visible lesions or spots",
83
+ "Normal leaf structure and growth",
84
+ "Good plant vigor"
85
+ ],
86
+ "solutions": [
87
+ "Continue current management practices",
88
+ "Monitor regularly for early disease detection",
89
+ "Maintain balanced nutrition program",
90
+ "Ensure adequate water management"
91
+ ],
92
+ "prevention": [
93
+ "Use certified seeds",
94
+ "Implement integrated pest management",
95
+ "Maintain soil health",
96
+ "Practice crop rotation"
97
+ ]
98
+ },
99
+ {
100
+ "crop": "Potato",
101
+ "disease": "Early_Blight",
102
+ "description": "Caused by Alternaria solani fungus. Creates dark brown spots with concentric rings on leaves. Thrives in warm, humid conditions and can affect tubers.",
103
+ "symptoms": [
104
+ "Dark brown spots with concentric rings",
105
+ "Target-like lesions on leaves",
106
+ "Yellow halos around spots",
107
+ "Premature defoliation",
108
+ "Tuber lesions with dark, sunken areas"
109
+ ],
110
+ "solutions": [
111
+ "Apply fungicides containing chlorothalonil or copper",
112
+ "Remove infected plant debris",
113
+ "Ensure proper plant spacing for air circulation",
114
+ "Avoid overhead irrigation",
115
+ "Harvest tubers when mature and dry"
116
+ ],
117
+ "prevention": [
118
+ "Plant certified disease-free seed potatoes",
119
+ "Rotate crops with non-solanaceous plants",
120
+ "Maintain balanced nutrition",
121
+ "Avoid water stress"
122
+ ]
123
+ },
124
+ {
125
+ "crop": "Potato",
126
+ "disease": "Late_Blight",
127
+ "description": "Caused by Phytophthora infestans. Most destructive potato disease. Creates water-soaked lesions that turn brown. Spreads rapidly in cool, humid conditions.",
128
+ "symptoms": [
129
+ "Water-soaked lesions on leaves",
130
+ "Brown to black lesions with yellow margins",
131
+ "White fuzzy growth on leaf undersides",
132
+ "Rapid plant collapse",
133
+ "Tuber rot with reddish-brown discoloration"
134
+ ],
135
+ "solutions": [
136
+ "Apply preventive fungicides (metalaxyl, mancozeb)",
137
+ "Remove infected plants immediately",
138
+ "Improve field drainage",
139
+ "Avoid irrigation during humid periods",
140
+ "Harvest before disease spreads to tubers"
141
+ ],
142
+ "prevention": [
143
+ "Use resistant potato varieties",
144
+ "Plant certified seed potatoes",
145
+ "Monitor weather conditions closely",
146
+ "Implement strict sanitation practices"
147
+ ]
148
+ },
149
+ {
150
+ "crop": "Potato",
151
+ "disease": "healthy",
152
+ "description": "Healthy potato plants display vigorous growth with dark green foliage. Proper cultural practices maintain plant health and maximize yield potential.",
153
+ "symptoms": [
154
+ "Dark green, vigorous foliage",
155
+ "No disease symptoms present",
156
+ "Normal plant architecture",
157
+ "Good tuber development"
158
+ ],
159
+ "solutions": [
160
+ "Continue current management practices",
161
+ "Monitor for early disease symptoms",
162
+ "Maintain optimal growing conditions",
163
+ "Implement preventive measures"
164
+ ],
165
+ "prevention": [
166
+ "Use certified seed potatoes",
167
+ "Practice crop rotation",
168
+ "Maintain soil health",
169
+ "Monitor environmental conditions"
170
+ ]
171
+ },
172
+ {
173
+ "crop": "Tomato",
174
+ "disease": "Bacterial_spot",
175
+ "description": "Caused by Xanthomonas species bacteria. Creates small, dark spots on leaves, stems, and fruit. Spreads through water splash and contaminated tools.",
176
+ "symptoms": [
177
+ "Small, dark brown to black spots on leaves",
178
+ "Spots may have yellow halos",
179
+ "Fruit spots are raised and scab-like",
180
+ "Severe defoliation in advanced stages"
181
+ ],
182
+ "solutions": [
183
+ "Apply copper-based bactericides",
184
+ "Remove infected plant material",
185
+ "Avoid overhead irrigation",
186
+ "Disinfect tools between plants",
187
+ "Improve air circulation"
188
+ ],
189
+ "prevention": [
190
+ "Use certified disease-free seeds",
191
+ "Practice crop rotation",
192
+ "Avoid working in wet fields",
193
+ "Maintain proper plant spacing"
194
+ ]
195
+ },
196
+ {
197
+ "crop": "Tomato",
198
+ "disease": "Early_blight",
199
+ "description": "Caused by Alternaria solani fungus. Creates dark spots with concentric rings on lower leaves first. Common in warm, humid conditions.",
200
+ "symptoms": [
201
+ "Dark brown spots with concentric rings",
202
+ "Target-like lesions starting on lower leaves",
203
+ "Yellow halos around spots",
204
+ "Progressive upward movement",
205
+ "Fruit lesions near stem end"
206
+ ],
207
+ "solutions": [
208
+ "Apply fungicides containing chlorothalonil",
209
+ "Remove lower infected leaves",
210
+ "Mulch around plants to prevent soil splash",
211
+ "Ensure adequate plant spacing",
212
+ "Water at soil level"
213
+ ],
214
+ "prevention": [
215
+ "Use resistant varieties when available",
216
+ "Rotate crops annually",
217
+ "Maintain balanced nutrition",
218
+ "Avoid overhead watering"
219
+ ]
220
+ },
221
+ {
222
+ "crop": "Tomato",
223
+ "disease": "Late_blight",
224
+ "description": "Caused by Phytophthora infestans. Devastating disease that can destroy entire crops quickly. Favored by cool, wet conditions.",
225
+ "symptoms": [
226
+ "Water-soaked lesions on leaves",
227
+ "Brown to black lesions with yellow margins",
228
+ "White fuzzy growth on leaf undersides",
229
+ "Rapid plant collapse",
230
+ "Fruit rot with firm, brown lesions"
231
+ ],
232
+ "solutions": [
233
+ "Apply preventive fungicides immediately",
234
+ "Remove infected plants completely",
235
+ "Improve air circulation",
236
+ "Avoid overhead irrigation",
237
+ "Harvest green fruit before infection"
238
+ ],
239
+ "prevention": [
240
+ "Use resistant varieties",
241
+ "Monitor weather conditions",
242
+ "Ensure good drainage",
243
+ "Practice strict sanitation"
244
+ ]
245
+ },
246
+ {
247
+ "crop": "Tomato",
248
+ "disease": "Leaf_Mold",
249
+ "description": "Caused by Passalora fulva fungus. Primarily affects greenhouse tomatoes. Creates yellow spots on upper leaf surfaces with fuzzy growth underneath.",
250
+ "symptoms": [
251
+ "Yellow spots on upper leaf surfaces",
252
+ "Olive-green to brown fuzzy growth on undersides",
253
+ "Spots may merge causing leaf yellowing",
254
+ "Premature leaf drop"
255
+ ],
256
+ "solutions": [
257
+ "Improve greenhouse ventilation",
258
+ "Reduce humidity levels",
259
+ "Apply fungicides if necessary",
260
+ "Remove infected leaves",
261
+ "Space plants adequately"
262
+ ],
263
+ "prevention": [
264
+ "Use resistant varieties",
265
+ "Maintain proper humidity control",
266
+ "Ensure adequate air circulation",
267
+ "Avoid overhead watering"
268
+ ]
269
+ },
270
+ {
271
+ "crop": "Tomato",
272
+ "disease": "Septoria_leaf_spot",
273
+ "description": "Caused by Septoria lycopersici fungus. Creates small, circular spots with gray centers and dark borders. Starts on lower leaves and moves upward.",
274
+ "symptoms": [
275
+ "Small circular spots with gray centers",
276
+ "Dark brown to black borders",
277
+ "Tiny black specks in spot centers",
278
+ "Progressive upward movement",
279
+ "Severe defoliation possible"
280
+ ],
281
+ "solutions": [
282
+ "Apply fungicides containing chlorothalonil",
283
+ "Remove infected lower leaves",
284
+ "Mulch to prevent soil splash",
285
+ "Improve air circulation",
286
+ "Water at soil level"
287
+ ],
288
+ "prevention": [
289
+ "Use certified disease-free seeds",
290
+ "Practice crop rotation",
291
+ "Maintain proper plant spacing",
292
+ "Avoid overhead irrigation"
293
+ ]
294
+ },
295
+ {
296
+ "crop": "Tomato",
297
+ "disease": "Spider_mites_Two_spotted_spider_mite",
298
+ "description": "Caused by Tetranychus urticae. Tiny arachnids that feed on plant sap. Create stippling damage and fine webbing. Thrive in hot, dry conditions.",
299
+ "symptoms": [
300
+ "Fine stippling or speckling on leaves",
301
+ "Yellow or bronze leaf discoloration",
302
+ "Fine webbing on leaves and stems",
303
+ "Premature leaf drop",
304
+ "Reduced plant vigor"
305
+ ],
306
+ "solutions": [
307
+ "Apply miticides or insecticidal soaps",
308
+ "Increase humidity around plants",
309
+ "Use predatory mites as biological control",
310
+ "Remove heavily infested leaves",
311
+ "Spray with water to dislodge mites"
312
+ ],
313
+ "prevention": [
314
+ "Maintain adequate soil moisture",
315
+ "Avoid over-fertilization with nitrogen",
316
+ "Monitor plants regularly",
317
+ "Encourage beneficial insects"
318
+ ]
319
+ },
320
+ {
321
+ "crop": "Tomato",
322
+ "disease": "Target_Spot",
323
+ "description": "Caused by Corynespora cassiicola fungus. Creates circular spots with concentric rings resembling targets. Affects leaves, stems, and fruit.",
324
+ "symptoms": [
325
+ "Circular spots with concentric rings",
326
+ "Brown to gray centers with dark borders",
327
+ "Target-like appearance",
328
+ "Spots on leaves, stems, and fruit",
329
+ "Premature defoliation"
330
+ ],
331
+ "solutions": [
332
+ "Apply fungicides containing azoxystrobin",
333
+ "Remove infected plant debris",
334
+ "Improve air circulation",
335
+ "Avoid overhead irrigation",
336
+ "Practice crop rotation"
337
+ ],
338
+ "prevention": [
339
+ "Use resistant varieties when available",
340
+ "Maintain proper plant spacing",
341
+ "Ensure good drainage",
342
+ "Monitor humidity levels"
343
+ ]
344
+ },
345
+ {
346
+ "crop": "Tomato",
347
+ "disease": "Tomato_mosaic_virus",
348
+ "description": "Viral disease causing mosaic patterns on leaves. Transmitted through infected seeds, tools, and handling. No cure available once infected.",
349
+ "symptoms": [
350
+ "Mosaic pattern of light and dark green on leaves",
351
+ "Leaf distortion and curling",
352
+ "Stunted plant growth",
353
+ "Reduced fruit quality",
354
+ "Mottled fruit appearance"
355
+ ],
356
+ "solutions": [
357
+ "Remove infected plants immediately",
358
+ "Disinfect tools with bleach solution",
359
+ "Control aphid vectors",
360
+ "Avoid handling plants when wet",
361
+ "Use virus-free transplants"
362
+ ],
363
+ "prevention": [
364
+ "Use certified virus-free seeds",
365
+ "Practice strict sanitation",
366
+ "Control insect vectors",
367
+ "Avoid tobacco use around plants"
368
+ ]
369
+ },
370
+ {
371
+ "crop": "Tomato",
372
+ "disease": "Tomato_Yellow_Leaf_Curl_Virus",
373
+ "description": "Viral disease transmitted by whiteflies. Causes severe leaf curling and yellowing. Can devastate tomato crops in warm climates.",
374
+ "symptoms": [
375
+ "Severe upward leaf curling",
376
+ "Yellow leaf margins",
377
+ "Stunted plant growth",
378
+ "Reduced fruit set",
379
+ "Small, poor-quality fruit"
380
+ ],
381
+ "solutions": [
382
+ "Control whitefly populations",
383
+ "Remove infected plants",
384
+ "Use reflective mulches",
385
+ "Apply insecticides for whitefly control",
386
+ "Use physical barriers"
387
+ ],
388
+ "prevention": [
389
+ "Use resistant varieties",
390
+ "Control whitefly vectors",
391
+ "Remove weeds that harbor whiteflies",
392
+ "Monitor plants regularly"
393
+ ]
394
+ },
395
+ {
396
+ "crop": "Tomato",
397
+ "disease": "healthy",
398
+ "description": "Healthy tomato plants exhibit vigorous growth with dark green foliage and normal fruit development. Proper care maintains optimal plant health.",
399
+ "symptoms": [
400
+ "Dark green, vigorous foliage",
401
+ "Normal leaf shape and size",
402
+ "Good fruit set and development",
403
+ "No visible disease symptoms"
404
+ ],
405
+ "solutions": [
406
+ "Continue current management practices",
407
+ "Monitor for early disease detection",
408
+ "Maintain optimal growing conditions",
409
+ "Implement preventive measures"
410
+ ],
411
+ "prevention": [
412
+ "Use certified disease-free seeds",
413
+ "Practice crop rotation",
414
+ "Maintain balanced nutrition",
415
+ "Ensure proper watering practices"
416
+ ]
417
+ }
418
+ ]
419
+ }
knowledge_base/disease_info_updated.json ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_info": {
3
+ "model_name": "Crop Disease Detection - Retrained ResNet50",
4
+ "version": "3.0",
5
+ "last_updated": "2025-09-09",
6
+ "training_dataset": "Pepper, Potato, and Tomato Disease Dataset",
7
+ "total_classes": 15,
8
+ "test_accuracy": 0.9009,
9
+ "validation_accuracy": 0.9006,
10
+ "model_file": "models/crop_disease_retrained_final.pth",
11
+ "training_samples": 14440,
12
+ "validation_samples": 3089,
13
+ "test_samples": 3109,
14
+ "supported_crops": ["Pepper (Bell)", "Potato", "Tomato"]
15
+ },
16
+ "diseases": [
17
+ {
18
+ "crop": "Pepper (Bell)",
19
+ "disease": "Bacterial_spot",
20
+ "class_name": "Pepper__bell___Bacterial_spot",
21
+ "description": "Caused by Xanthomonas bacteria. Creates dark, water-soaked spots on leaves, stems, and fruits. Thrives in warm, humid conditions with overhead irrigation.",
22
+ "symptoms": [
23
+ "Small, dark brown to black spots on leaves",
24
+ "Water-soaked appearance of lesions",
25
+ "Yellow halos around older spots",
26
+ "Spots on fruits appear raised and scabby",
27
+ "Premature defoliation in severe cases"
28
+ ],
29
+ "solutions": [
30
+ "Apply copper-based bactericides preventively",
31
+ "Use bacterial spot resistant varieties",
32
+ "Improve air circulation around plants",
33
+ "Avoid overhead irrigation",
34
+ "Remove infected plant debris",
35
+ "Practice crop rotation with non-host crops"
36
+ ],
37
+ "prevention": [
38
+ "Use certified disease-free seeds and transplants",
39
+ "Avoid working in fields when plants are wet",
40
+ "Maintain proper plant spacing",
41
+ "Apply drip irrigation instead of overhead watering",
42
+ "Sanitize tools between plants"
43
+ ],
44
+ "severity": "High",
45
+ "confidence_threshold": 0.92
46
+ },
47
+ {
48
+ "crop": "Pepper (Bell)",
49
+ "disease": "Healthy",
50
+ "class_name": "Pepper__bell___healthy",
51
+ "description": "Healthy bell pepper plants with no visible disease symptoms.",
52
+ "symptoms": [
53
+ "Vibrant green, uniform colored leaves",
54
+ "No spots, lesions, or discoloration",
55
+ "Normal plant growth and development",
56
+ "Healthy fruit development"
57
+ ],
58
+ "solutions": [
59
+ "Continue current management practices",
60
+ "Maintain regular monitoring",
61
+ "Ensure proper nutrition and watering"
62
+ ],
63
+ "prevention": [
64
+ "Regular inspection for early disease detection",
65
+ "Proper sanitation practices",
66
+ "Balanced fertilization",
67
+ "Appropriate irrigation management"
68
+ ],
69
+ "severity": "None",
70
+ "confidence_threshold": 0.95
71
+ },
72
+ {
73
+ "crop": "Potato",
74
+ "disease": "Early_blight",
75
+ "class_name": "Potato___Early_blight",
76
+ "description": "Caused by Alternaria solani fungus. Creates concentric ring patterns on leaves and can affect tubers. Common in warm, humid conditions with plant stress.",
77
+ "symptoms": [
78
+ "Dark brown spots with concentric rings (target spots)",
79
+ "Yellow halos around lesions",
80
+ "Lower leaves affected first",
81
+ "V-shaped lesions at leaf margins",
82
+ "Premature defoliation",
83
+ "Dark, sunken spots on tubers"
84
+ ],
85
+ "solutions": [
86
+ "Apply fungicides containing chlorothalonil or mancozeb",
87
+ "Remove infected lower leaves",
88
+ "Improve air circulation",
89
+ "Avoid overhead irrigation",
90
+ "Apply balanced fertilization",
91
+ "Harvest tubers when skin is set"
92
+ ],
93
+ "prevention": [
94
+ "Plant certified disease-free seed potatoes",
95
+ "Rotate crops with non-solanaceous plants",
96
+ "Maintain proper plant nutrition",
97
+ "Avoid plant stress from drought or excess nitrogen",
98
+ "Clean up crop debris after harvest"
99
+ ],
100
+ "severity": "Medium to High",
101
+ "confidence_threshold": 0.96
102
+ },
103
+ {
104
+ "crop": "Potato",
105
+ "disease": "Late_blight",
106
+ "class_name": "Potato___Late_blight",
107
+ "description": "Caused by Phytophthora infestans. The same pathogen that caused the Irish Potato Famine. Spreads rapidly in cool, wet conditions.",
108
+ "symptoms": [
109
+ "Water-soaked, dark green to brown lesions",
110
+ "White, fuzzy sporulation on leaf undersides",
111
+ "Rapid spread during cool, wet weather",
112
+ "Blackening and collapse of stems",
113
+ "Firm, brown rot of tubers",
114
+ "Entire plant death possible"
115
+ ],
116
+ "solutions": [
117
+ "Apply preventive fungicides (metalaxyl, chlorothalonil)",
118
+ "Remove infected plants immediately",
119
+ "Improve drainage and air circulation",
120
+ "Monitor weather conditions closely",
121
+ "Destroy volunteer potatoes",
122
+ "Cure tubers properly before storage"
123
+ ],
124
+ "prevention": [
125
+ "Plant certified seed potatoes",
126
+ "Choose late blight resistant varieties",
127
+ "Avoid overhead irrigation",
128
+ "Hill soil properly around plants",
129
+ "Monitor local disease forecasts",
130
+ "Destroy cull piles and volunteers"
131
+ ],
132
+ "severity": "Very High",
133
+ "confidence_threshold": 0.86
134
+ },
135
+ {
136
+ "crop": "Potato",
137
+ "disease": "Healthy",
138
+ "class_name": "Potato___healthy",
139
+ "description": "Healthy potato plants with no visible disease symptoms.",
140
+ "symptoms": [
141
+ "Bright green, healthy foliage",
142
+ "Normal plant growth and development",
143
+ "No spots, lesions, or discoloration",
144
+ "Vigorous stem growth"
145
+ ],
146
+ "solutions": [
147
+ "Continue current management practices",
148
+ "Maintain regular monitoring",
149
+ "Ensure proper nutrition and watering"
150
+ ],
151
+ "prevention": [
152
+ "Regular field inspection",
153
+ "Proper crop rotation",
154
+ "Balanced fertilization",
155
+ "Appropriate irrigation management"
156
+ ],
157
+ "severity": "None",
158
+ "confidence_threshold": 0.38
159
+ },
160
+ {
161
+ "crop": "Tomato",
162
+ "disease": "Target_Spot",
163
+ "class_name": "Tomato__Target_Spot",
164
+ "description": "Caused by Corynespora cassiicola fungus. Creates concentric ring spots on leaves and can affect fruits. Favored by warm, humid conditions.",
165
+ "symptoms": [
166
+ "Small, dark brown spots with concentric rings",
167
+ "Target-like appearance of lesions",
168
+ "Yellow halos around spots",
169
+ "Spots can merge causing leaf blight",
170
+ "Fruit lesions are dark and sunken",
171
+ "Premature defoliation"
172
+ ],
173
+ "solutions": [
174
+ "Apply fungicides containing azoxystrobin or chlorothalonil",
175
+ "Remove infected plant debris",
176
+ "Improve air circulation",
177
+ "Avoid overhead watering",
178
+ "Practice crop rotation",
179
+ "Remove lower leaves touching soil"
180
+ ],
181
+ "prevention": [
182
+ "Plant resistant varieties when available",
183
+ "Maintain proper plant spacing",
184
+ "Use drip irrigation",
185
+ "Apply mulch to prevent soil splash",
186
+ "Sanitize tools and equipment"
187
+ ],
188
+ "severity": "Medium",
189
+ "confidence_threshold": 0.87
190
+ },
191
+ {
192
+ "crop": "Tomato",
193
+ "disease": "Tomato_mosaic_virus",
194
+ "class_name": "Tomato__Tomato_mosaic_virus",
195
+ "description": "Viral disease causing mosaic patterns on leaves. Transmitted through infected seeds, mechanical transmission, and contaminated tools.",
196
+ "symptoms": [
197
+ "Light and dark green mosaic pattern on leaves",
198
+ "Mottled appearance of foliage",
199
+ "Stunted plant growth",
200
+ "Reduced fruit size and yield",
201
+ "Fruit may show irregular ripening",
202
+ "Leaf distortion and curling"
203
+ ],
204
+ "solutions": [
205
+ "Remove infected plants immediately",
206
+ "Control aphid vectors",
207
+ "Sanitize tools with 10% bleach solution",
208
+ "Avoid smoking near plants",
209
+ "Use virus-free transplants",
210
+ "Control weeds that may harbor virus"
211
+ ],
212
+ "prevention": [
213
+ "Use certified virus-free seeds",
214
+ "Wash hands before handling plants",
215
+ "Avoid mechanical transmission",
216
+ "Control insect vectors",
217
+ "Remove infected plant debris",
218
+ "Practice crop rotation"
219
+ ],
220
+ "severity": "High",
221
+ "confidence_threshold": 0.67
222
+ },
223
+ {
224
+ "crop": "Tomato",
225
+ "disease": "Tomato_YellowLeaf_Curl_Virus",
226
+ "class_name": "Tomato__Tomato_YellowLeaf__Curl_Virus",
227
+ "description": "Viral disease transmitted by whiteflies. Causes severe yield losses in warm climates. One of the most devastating tomato diseases.",
228
+ "symptoms": [
229
+ "Upward curling and yellowing of leaves",
230
+ "Stunted plant growth",
231
+ "Reduced fruit set and size",
232
+ "Thick, leathery leaf texture",
233
+ "Purple veins on leaf undersides",
234
+ "Severe yield reduction"
235
+ ],
236
+ "solutions": [
237
+ "Control whitefly vectors with insecticides",
238
+ "Remove infected plants",
239
+ "Use reflective mulches",
240
+ "Apply systemic insecticides",
241
+ "Install fine mesh screens in greenhouses",
242
+ "Monitor and trap whiteflies"
243
+ ],
244
+ "prevention": [
245
+ "Plant resistant varieties",
246
+ "Control whitefly populations",
247
+ "Remove weeds that harbor whiteflies",
248
+ "Use yellow sticky traps",
249
+ "Avoid planting near infected crops",
250
+ "Time planting to avoid peak whitefly populations"
251
+ ],
252
+ "severity": "Very High",
253
+ "confidence_threshold": 0.94
254
+ },
255
+ {
256
+ "crop": "Tomato",
257
+ "disease": "Bacterial_spot",
258
+ "class_name": "Tomato_Bacterial_spot",
259
+ "description": "Caused by Xanthomonas bacteria. Creates dark spots on leaves and fruits. Spreads rapidly in warm, wet conditions.",
260
+ "symptoms": [
261
+ "Small, dark brown to black spots on leaves",
262
+ "Water-soaked appearance of lesions",
263
+ "Yellow halos around spots",
264
+ "Raised, scabby spots on fruits",
265
+ "Premature defoliation",
266
+ "Fruit cracking and secondary infections"
267
+ ],
268
+ "solutions": [
269
+ "Apply copper-based bactericides",
270
+ "Use resistant varieties",
271
+ "Improve air circulation",
272
+ "Avoid overhead irrigation",
273
+ "Remove infected plant debris",
274
+ "Practice crop rotation"
275
+ ],
276
+ "prevention": [
277
+ "Use certified disease-free seeds",
278
+ "Avoid working in wet fields",
279
+ "Maintain proper plant spacing",
280
+ "Use drip irrigation systems",
281
+ "Sanitize tools between plants",
282
+ "Remove volunteer tomatoes"
283
+ ],
284
+ "severity": "High",
285
+ "confidence_threshold": 0.94
286
+ },
287
+ {
288
+ "crop": "Tomato",
289
+ "disease": "Early_blight",
290
+ "class_name": "Tomato_Early_blight",
291
+ "description": "Caused by Alternaria solani fungus. Creates target spot lesions on leaves and can affect fruits. Common during warm, humid periods.",
292
+ "symptoms": [
293
+ "Dark brown spots with concentric rings",
294
+ "Target-like appearance of lesions",
295
+ "Yellow halos around spots",
296
+ "Lower leaves affected first",
297
+ "Premature defoliation",
298
+ "Dark, sunken spots on fruits"
299
+ ],
300
+ "solutions": [
301
+ "Apply fungicides containing chlorothalonil",
302
+ "Remove infected lower leaves",
303
+ "Improve air circulation",
304
+ "Avoid overhead watering",
305
+ "Apply balanced fertilization",
306
+ "Stake plants properly"
307
+ ],
308
+ "prevention": [
309
+ "Use certified disease-free transplants",
310
+ "Rotate crops with non-solanaceous plants",
311
+ "Maintain proper plant nutrition",
312
+ "Mulch around plants",
313
+ "Remove crop debris after harvest",
314
+ "Space plants adequately"
315
+ ],
316
+ "severity": "Medium",
317
+ "confidence_threshold": 0.77
318
+ },
319
+ {
320
+ "crop": "Tomato",
321
+ "disease": "Healthy",
322
+ "class_name": "Tomato_healthy",
323
+ "description": "Healthy tomato plants with no visible disease symptoms.",
324
+ "symptoms": [
325
+ "Vibrant green, healthy foliage",
326
+ "Normal plant growth and development",
327
+ "No spots, lesions, or discoloration",
328
+ "Healthy fruit development",
329
+ "Strong stem structure"
330
+ ],
331
+ "solutions": [
332
+ "Continue current management practices",
333
+ "Maintain regular monitoring",
334
+ "Ensure proper nutrition and watering"
335
+ ],
336
+ "prevention": [
337
+ "Regular inspection for early disease detection",
338
+ "Proper sanitation practices",
339
+ "Balanced fertilization",
340
+ "Appropriate irrigation management"
341
+ ],
342
+ "severity": "None",
343
+ "confidence_threshold": 0.97
344
+ },
345
+ {
346
+ "crop": "Tomato",
347
+ "disease": "Late_blight",
348
+ "class_name": "Tomato_Late_blight",
349
+ "description": "Caused by Phytophthora infestans. Rapid-spreading disease favored by cool, wet conditions. Can destroy entire crops quickly.",
350
+ "symptoms": [
351
+ "Water-soaked, dark green lesions",
352
+ "White, fuzzy sporulation on leaf undersides",
353
+ "Rapid spread during cool, wet weather",
354
+ "Brown, firm rot of fruits",
355
+ "Blackening of stems",
356
+ "Collapse of entire plant"
357
+ ],
358
+ "solutions": [
359
+ "Apply preventive fungicides immediately",
360
+ "Remove infected plants",
361
+ "Improve drainage and air circulation",
362
+ "Monitor weather conditions",
363
+ "Destroy infected fruit and debris",
364
+ "Harvest healthy fruits early"
365
+ ],
366
+ "prevention": [
367
+ "Plant resistant varieties",
368
+ "Avoid overhead irrigation",
369
+ "Ensure good air circulation",
370
+ "Monitor disease forecasts",
371
+ "Remove volunteer tomatoes",
372
+ "Practice crop rotation"
373
+ ],
374
+ "severity": "Very High",
375
+ "confidence_threshold": 0.86
376
+ },
377
+ {
378
+ "crop": "Tomato",
379
+ "disease": "Leaf_Mold",
380
+ "class_name": "Tomato_Leaf_Mold",
381
+ "description": "Caused by Passalora fulva fungus. Primarily affects greenhouse tomatoes. Thrives in high humidity conditions.",
382
+ "symptoms": [
383
+ "Yellow spots on upper leaf surfaces",
384
+ "Olive-green, velvety mold on leaf undersides",
385
+ "Progressive yellowing and browning",
386
+ "Premature defoliation",
387
+ "Reduced fruit quality",
388
+ "Poor air circulation enhances disease"
389
+ ],
390
+ "solutions": [
391
+ "Improve ventilation in greenhouses",
392
+ "Reduce humidity levels",
393
+ "Apply fungicides containing chlorothalonil",
394
+ "Remove infected leaves",
395
+ "Increase plant spacing",
396
+ "Control temperature and humidity"
397
+ ],
398
+ "prevention": [
399
+ "Plant resistant varieties",
400
+ "Maintain proper ventilation",
401
+ "Control greenhouse humidity",
402
+ "Avoid overhead watering",
403
+ "Monitor environmental conditions",
404
+ "Remove crop debris"
405
+ ],
406
+ "severity": "Medium",
407
+ "confidence_threshold": 0.87
408
+ },
409
+ {
410
+ "crop": "Tomato",
411
+ "disease": "Septoria_leaf_spot",
412
+ "class_name": "Tomato_Septoria_leaf_spot",
413
+ "description": "Caused by Septoria lycopersici fungus. Creates small circular spots with dark borders. Favored by warm, wet weather.",
414
+ "symptoms": [
415
+ "Small, circular spots with dark borders",
416
+ "Gray to tan center with dark margin",
417
+ "Tiny black specks (pycnidia) in spot centers",
418
+ "Yellow halos around spots",
419
+ "Lower leaves affected first",
420
+ "Progressive defoliation upward"
421
+ ],
422
+ "solutions": [
423
+ "Apply fungicides containing chlorothalonil or copper",
424
+ "Remove infected lower leaves",
425
+ "Improve air circulation",
426
+ "Avoid overhead watering",
427
+ "Stake plants to improve airflow",
428
+ "Apply mulch to prevent soil splash"
429
+ ],
430
+ "prevention": [
431
+ "Rotate crops with non-solanaceous plants",
432
+ "Use certified disease-free transplants",
433
+ "Maintain proper plant spacing",
434
+ "Remove crop debris after harvest",
435
+ "Avoid working in wet fields",
436
+ "Apply balanced fertilization"
437
+ ],
438
+ "severity": "Medium",
439
+ "confidence_threshold": 0.91
440
+ },
441
+ {
442
+ "crop": "Tomato",
443
+ "disease": "Spider_mites_Two_spotted_spider_mite",
444
+ "class_name": "Tomato_Spider_mites_Two_spotted_spider_mite",
445
+ "description": "Caused by Tetranychus urticae. Tiny arachnids that feed on plant cells. Thrive in hot, dry conditions.",
446
+ "symptoms": [
447
+ "Fine stippling or speckling on leaves",
448
+ "Yellow or bronze discoloration",
449
+ "Fine webbing on leaves and stems",
450
+ "Premature leaf drop",
451
+ "Reduced plant vigor",
452
+ "Severe infestations cause plant death"
453
+ ],
454
+ "solutions": [
455
+ "Apply miticides or insecticidal soaps",
456
+ "Increase humidity around plants",
457
+ "Release predatory mites",
458
+ "Spray with water to dislodge mites",
459
+ "Remove heavily infested leaves",
460
+ "Apply reflective mulches"
461
+ ],
462
+ "prevention": [
463
+ "Maintain adequate soil moisture",
464
+ "Avoid over-fertilization with nitrogen",
465
+ "Monitor plants regularly",
466
+ "Encourage beneficial insects",
467
+ "Remove weeds that harbor mites",
468
+ "Avoid dusty conditions"
469
+ ],
470
+ "severity": "Medium to High",
471
+ "confidence_threshold": 0.91
472
+ }
473
+ ],
474
+ "treatment_recommendations": {
475
+ "organic_treatments": {
476
+ "fungal_diseases": [
477
+ "Copper-based fungicides",
478
+ "Baking soda solutions",
479
+ "Neem oil applications",
480
+ "Compost tea sprays",
481
+ "Milk solutions (for powdery mildew)"
482
+ ],
483
+ "bacterial_diseases": [
484
+ "Copper sulfate treatments",
485
+ "Hydrogen peroxide solutions",
486
+ "Essential oil treatments",
487
+ "Proper sanitation practices"
488
+ ],
489
+ "viral_diseases": [
490
+ "Remove infected plants",
491
+ "Control insect vectors",
492
+ "Use resistant varieties",
493
+ "Improve sanitation"
494
+ ],
495
+ "pest_management": [
496
+ "Beneficial insect release",
497
+ "Insecticidal soaps",
498
+ "Diatomaceous earth",
499
+ "Row covers for protection"
500
+ ]
501
+ },
502
+ "chemical_treatments": {
503
+ "systemic_fungicides": [
504
+ "Azoxystrobin",
505
+ "Propiconazole",
506
+ "Metalaxyl",
507
+ "Tebuconazole"
508
+ ],
509
+ "contact_fungicides": [
510
+ "Chlorothalonil",
511
+ "Mancozeb",
512
+ "Copper compounds",
513
+ "Sulfur"
514
+ ],
515
+ "bactericides": [
516
+ "Copper hydroxide",
517
+ "Copper oxychloride",
518
+ "Streptomycin (where permitted)"
519
+ ],
520
+ "insecticides": [
521
+ "Imidacloprid",
522
+ "Thiamethoxam",
523
+ "Spinosad",
524
+ "Bifenthrin"
525
+ ]
526
+ }
527
+ },
528
+ "general_prevention": {
529
+ "cultural_practices": [
530
+ "Crop rotation with non-host plants",
531
+ "Proper plant spacing for air circulation",
532
+ "Drip irrigation instead of overhead watering",
533
+ "Mulching to prevent soil splash",
534
+ "Regular field sanitation",
535
+ "Removal of crop debris"
536
+ ],
537
+ "monitoring": [
538
+ "Regular field inspections",
539
+ "Weather monitoring for disease-favorable conditions",
540
+ "Early detection and rapid response",
541
+ "Record keeping of disease occurrences",
542
+ "Soil and plant tissue testing"
543
+ ],
544
+ "resistance_management": [
545
+ "Use of resistant varieties",
546
+ "Rotation of fungicide modes of action",
547
+ "Integrated pest management approaches",
548
+ "Maintaining genetic diversity in crops"
549
+ ]
550
+ }
551
+ }
models/.gitattributes ADDED
@@ -0,0 +1 @@
 
 
1
+ *.pth filter=lfs diff=lfs merge=lfs -text
models/README.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Model Checkpoints Directory
2
+ ===========================
3
+
4
+ This directory will store trained model files:
5
+ - crop_disease_resnet50.pth (main trained model)
6
+ - Best model checkpoints during training
7
+ - Model configuration files
models/crop_disease_v2_model.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:29380e58e90eefded1f97484f3683aae59478d8c84fc53a20fef6dc3ee285024
3
+ size 104911995
models/crop_disease_v3_model.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dd0cb08c7687522f57ed00b8f1a1aa0678bed108fe5479d4f974c525cc6cf331
3
+ size 104915719
notebooks/train_resnet50.ipynb ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Crop Disease Detection - ResNet50 Training\n",
8
+ "\n",
9
+ "This notebook implements the training pipeline for crop disease detection using ResNet50 transfer learning."
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": null,
15
+ "metadata": {},
16
+ "outputs": [],
17
+ "source": [
18
+ "# Import required libraries\n",
19
+ "import sys\n",
20
+ "sys.path.append('../src')\n",
21
+ "\n",
22
+ "import torch\n",
23
+ "import torch.nn as nn\n",
24
+ "import torch.optim as optim\n",
25
+ "from torch.utils.data import DataLoader\n",
26
+ "import matplotlib.pyplot as plt\n",
27
+ "import numpy as np\n",
28
+ "\n",
29
+ "from dataset import create_data_loaders\n",
30
+ "from model import create_model, get_model_summary\n",
31
+ "from train import Trainer\n",
32
+ "\n",
33
+ "print(\"Libraries imported successfully!\")"
34
+ ]
35
+ },
36
+ {
37
+ "cell_type": "code",
38
+ "execution_count": null,
39
+ "metadata": {},
40
+ "outputs": [],
41
+ "source": [
42
+ "# Configuration\n",
43
+ "config = {\n",
44
+ " 'data_dir': '../data',\n",
45
+ " 'batch_size': 8, # Small batch size for demo\n",
46
+ " 'num_epochs': 5, # Reduced for quick training\n",
47
+ " 'learning_rate': 1e-4,\n",
48
+ " 'device': torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
49
+ "}\n",
50
+ "\n",
51
+ "print(f\"Using device: {config['device']}\")\n",
52
+ "print(f\"Configuration: {config}\")"
53
+ ]
54
+ },
55
+ {
56
+ "cell_type": "code",
57
+ "execution_count": null,
58
+ "metadata": {},
59
+ "outputs": [],
60
+ "source": [
61
+ "# Load dataset\n",
62
+ "print(\"Loading dataset...\")\n",
63
+ "train_loader, val_loader, test_loader, class_names = create_data_loaders(\n",
64
+ " data_dir=config['data_dir'],\n",
65
+ " batch_size=config['batch_size'],\n",
66
+ " num_workers=0\n",
67
+ ")\n",
68
+ "\n",
69
+ "print(f\"Dataset loaded successfully!\")\n",
70
+ "print(f\"Number of classes: {len(class_names)}\")\n",
71
+ "print(f\"Classes: {class_names}\")\n",
72
+ "print(f\"Training samples: {len(train_loader.dataset)}\")\n",
73
+ "print(f\"Validation samples: {len(val_loader.dataset)}\")\n",
74
+ "print(f\"Test samples: {len(test_loader.dataset)}\")"
75
+ ]
76
+ },
77
+ {
78
+ "cell_type": "code",
79
+ "execution_count": null,
80
+ "metadata": {},
81
+ "outputs": [],
82
+ "source": [
83
+ "# Create model\n",
84
+ "print(\"Creating ResNet50 model...\")\n",
85
+ "model = create_model(num_classes=len(class_names), device=config['device'])\n",
86
+ "get_model_summary(model)\n",
87
+ "\n",
88
+ "# Test forward pass\n",
89
+ "dummy_input = torch.randn(1, 3, 224, 224).to(config['device'])\n",
90
+ "output = model(dummy_input)\n",
91
+ "print(f\"\\nModel test - Input: {dummy_input.shape}, Output: {output.shape}\")"
92
+ ]
93
+ },
94
+ {
95
+ "cell_type": "code",
96
+ "execution_count": null,
97
+ "metadata": {},
98
+ "outputs": [],
99
+ "source": [
100
+ "# Initialize trainer\n",
101
+ "trainer = Trainer(model, train_loader, val_loader, class_names, config['device'])\n",
102
+ "\n",
103
+ "print(\"Trainer initialized successfully!\")\n",
104
+ "print(\"Ready to start training...\")"
105
+ ]
106
+ },
107
+ {
108
+ "cell_type": "code",
109
+ "execution_count": null,
110
+ "metadata": {},
111
+ "outputs": [],
112
+ "source": [
113
+ "# Start training\n",
114
+ "print(\"Starting training process...\")\n",
115
+ "\n",
116
+ "trained_model, history = trainer.train(\n",
117
+ " num_epochs=config['num_epochs'],\n",
118
+ " learning_rate=config['learning_rate'],\n",
119
+ " checkpoint_path='../models/crop_disease_resnet50.pth',\n",
120
+ " fine_tune_epoch=3 # Start fine-tuning earlier for demo\n",
121
+ ")\n",
122
+ "\n",
123
+ "print(\"\\nTraining completed!\")"
124
+ ]
125
+ },
126
+ {
127
+ "cell_type": "code",
128
+ "execution_count": null,
129
+ "metadata": {},
130
+ "outputs": [],
131
+ "source": [
132
+ "# Plot training results\n",
133
+ "trainer.plot_training_curves('../outputs/training_curves.png')\n",
134
+ "\n",
135
+ "# Display training history\n",
136
+ "print(\"Training History:\")\n",
137
+ "for epoch in range(len(history['train_loss'])):\n",
138
+ " print(f\"Epoch {epoch+1}: Train Loss: {history['train_loss'][epoch]:.4f}, \"\n",
139
+ " f\"Train Acc: {history['train_acc'][epoch]:.4f}, \"\n",
140
+ " f\"Val Loss: {history['val_loss'][epoch]:.4f}, \"\n",
141
+ " f\"Val Acc: {history['val_acc'][epoch]:.4f}\")"
142
+ ]
143
+ },
144
+ {
145
+ "cell_type": "code",
146
+ "execution_count": null,
147
+ "metadata": {},
148
+ "outputs": [],
149
+ "source": [
150
+ "# Evaluate model\n",
151
+ "from evaluate import evaluate_model\n",
152
+ "\n",
153
+ "print(\"Evaluating trained model...\")\n",
154
+ "results = evaluate_model(\n",
155
+ " checkpoint_path='../models/crop_disease_resnet50.pth',\n",
156
+ " data_dir='../data',\n",
157
+ " batch_size=config['batch_size']\n",
158
+ ")\n",
159
+ "\n",
160
+ "print(\"\\nModel evaluation completed!\")\n",
161
+ "print(f\"Final test accuracy: {results['metrics']['accuracy']:.4f}\")"
162
+ ]
163
+ },
164
+ {
165
+ "cell_type": "markdown",
166
+ "metadata": {},
167
+ "source": [
168
+ "## Training Complete!\n",
169
+ "\n",
170
+ "The ResNet50 model has been successfully trained for crop disease detection. The model checkpoint has been saved to `../models/crop_disease_resnet50.pth`.\n",
171
+ "\n",
172
+ "### Next Steps:\n",
173
+ "1. Implement knowledge base (Step 5)\n",
174
+ "2. Add Grad-CAM visualization (Step 6)\n",
175
+ "3. Build FastAPI backend (Step 8)\n",
176
+ "\n",
177
+ "### Files Generated:\n",
178
+ "- Model checkpoint: `models/crop_disease_resnet50.pth`\n",
179
+ "- Training curves: `outputs/training_curves.png`\n",
180
+ "- Evaluation results: `outputs/results.json`\n",
181
+ "- Confusion matrix: `outputs/confusion_matrix.png`"
182
+ ]
183
+ }
184
+ ],
185
+ "metadata": {
186
+ "kernelspec": {
187
+ "display_name": "Python 3",
188
+ "language": "python",
189
+ "name": "python3"
190
+ },
191
+ "language_info": {
192
+ "codemirror_mode": {
193
+ "name": "ipython",
194
+ "version": 3
195
+ },
196
+ "file_extension": ".py",
197
+ "mimetype": "text/x-python",
198
+ "name": "python",
199
+ "nbconvert_exporter": "python",
200
+ "pygments_lexer": "ipython3",
201
+ "version": "3.8.0"
202
+ }
203
+ },
204
+ "nbformat": 4,
205
+ "nbformat_minor": 4
206
+ }
outputs/comprehensive_evaluation_report.json ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "evaluation_summary": {
3
+ "report_generated": "2025-09-08T14:49:18.192053",
4
+ "models_evaluated": [
5
+ "V1_Baseline",
6
+ "V2_Enhanced"
7
+ ],
8
+ "dataset_characteristics": {
9
+ "total_classes": 17,
10
+ "class_distribution": "Balanced (3 samples per class in test set)",
11
+ "data_challenges": [
12
+ "Small dataset size (255 total samples)",
13
+ "Limited samples per class",
14
+ "Potential class imbalance in training"
15
+ ]
16
+ }
17
+ },
18
+ "performance_analysis": {
19
+ "V1": {
20
+ "final_validation_accuracy": 0.11764705882352941,
21
+ "best_validation_accuracy": 0.11764705882352941,
22
+ "final_training_accuracy": 0.1111111111111111,
23
+ "total_epochs": 20,
24
+ "overfitting_indicator": -0.006535947712418305
25
+ },
26
+ "V2": {
27
+ "final_validation_accuracy": 0.11764705882352941,
28
+ "best_validation_accuracy": 0.11764705882352941,
29
+ "test_accuracy": 0.11764705882352941,
30
+ "test_f1_score": 0.03725490196078431,
31
+ "total_epochs": 20,
32
+ "dataset_size": 153,
33
+ "model_improvements": [
34
+ "Enhanced data augmentation pipeline",
35
+ "Improved model architecture with BatchNorm",
36
+ "Label smoothing for better generalization",
37
+ "AdamW optimizer with weight decay",
38
+ "Cosine annealing learning rate schedule",
39
+ "Gradient clipping for training stability",
40
+ "F1-score based model selection"
41
+ ]
42
+ }
43
+ },
44
+ "key_findings": [
45
+ "\u26a0\ufe0f Low test accuracy (11.8%) indicates model struggles with current dataset",
46
+ "\u26a0\ufe0f Very low F1-score (0.037) suggests poor precision/recall balance",
47
+ "\ud83c\udfaf 15 classes have zero F1-score, indicating classification difficulties"
48
+ ],
49
+ "recommendations": [
50
+ "\ud83d\udcca Increase dataset size significantly (target: 1000+ samples per class)",
51
+ "\ud83d\udd04 Implement more aggressive data augmentation techniques",
52
+ "\u2696\ufe0f Address class imbalance with weighted sampling or SMOTE",
53
+ "\ud83e\udde0 Consider ensemble methods or different architectures (EfficientNet, Vision Transformer)",
54
+ "\ud83d\udcc8 Implement progressive resizing and test-time augmentation",
55
+ "\ud83c\udfaf Use focal loss or class-balanced loss functions",
56
+ "\ud83d\udd0d Perform detailed error analysis and confusion matrix review",
57
+ "\ud83d\udcdd Collect more diverse and representative training data"
58
+ ],
59
+ "next_steps": [
60
+ "\ud83d\udd2c Implement Grad-CAM visualization for model interpretability",
61
+ "\ud83c\udf10 Develop REST API for model deployment",
62
+ "\ud83d\udcf1 Create user-friendly frontend interface",
63
+ "\ud83e\uddea Set up continuous model evaluation pipeline",
64
+ "\ud83d\udcda Build knowledge base with disease information and remedies",
65
+ "\ud83d\ude80 Deploy model to cloud platform for scalability"
66
+ ]
67
+ }
outputs/v3_detailed_analysis.json ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "v3_analysis": {
3
+ "file_size_mb": 100.05542659759521,
4
+ "checkpoint_type": "dict",
5
+ "timestamp": "2025-09-09T16:36:11.468080",
6
+ "checkpoint_format": "full_checkpoint_with_metadata",
7
+ "num_classes": 15,
8
+ "class_names": [
9
+ "Pepper__bell___Bacterial_spot",
10
+ "Pepper__bell___healthy",
11
+ "Potato___Early_blight",
12
+ "Potato___healthy",
13
+ "Potato___Late_blight",
14
+ "Tomato__Target_Spot",
15
+ "Tomato__Tomato_mosaic_virus",
16
+ "Tomato__Tomato_YellowLeaf__Curl_Virus",
17
+ "Tomato_Bacterial_spot",
18
+ "Tomato_Early_blight",
19
+ "Tomato_healthy",
20
+ "Tomato_Late_blight",
21
+ "Tomato_Leaf_Mold",
22
+ "Tomato_Septoria_leaf_spot",
23
+ "Tomato_Spider_mites_Two_spotted_spider_mite"
24
+ ],
25
+ "best_val_acc": 0.9006150857882811,
26
+ "training_history": {
27
+ "train_loss": [
28
+ 1.4548105073767685,
29
+ 1.1704056904586728,
30
+ 1.1013115654691765,
31
+ 1.0586178303755551,
32
+ 1.075187584105621,
33
+ 1.0291379865682027,
34
+ 1.0170073299196618,
35
+ 1.0120474130328012,
36
+ 0.9980869875555224,
37
+ 0.9765783541255381,
38
+ 0.9738965100363681,
39
+ 0.9903353646686532,
40
+ 0.9741212320129627,
41
+ 0.9821224488049664,
42
+ 0.9913851496090189,
43
+ 0.9571966647606477,
44
+ 0.9684612701804354,
45
+ 0.9469294021664564,
46
+ 0.9630058443942558,
47
+ 0.9461190986336103,
48
+ 0.9484653520617129,
49
+ 0.952264212389732,
50
+ 0.9640788071703713,
51
+ 0.950173153788099,
52
+ 0.9554669914153144,
53
+ 0.9328232798880157,
54
+ 0.9476842921196259,
55
+ 0.9663470888071773,
56
+ 0.9468133702998016,
57
+ 0.9152398376748833,
58
+ 0.8808037900198199,
59
+ 0.8763768694407392,
60
+ 0.8685606538836645,
61
+ 0.8780639135771511,
62
+ 0.8459524025877427,
63
+ 0.8511842027760609,
64
+ 0.8601165542311946,
65
+ 0.8372113296034593,
66
+ 0.8474745948889248,
67
+ 0.8300480768786243,
68
+ 0.8198002664336207,
69
+ 0.8241935499487161,
70
+ 0.819708017018363,
71
+ 0.7974475429493965,
72
+ 0.8204424370358856,
73
+ 0.8034243753246983,
74
+ 0.7993240317809615,
75
+ 0.7915693512336038,
76
+ 0.7926952356529368,
77
+ 0.7982220532986596
78
+ ],
79
+ "train_acc": [
80
+ 0.5421052631578948,
81
+ 0.6243767313019392,
82
+ 0.6425900277008311,
83
+ 0.6549168975069253,
84
+ 0.6538781163434904,
85
+ 0.665443213296399,
86
+ 0.6729224376731302,
87
+ 0.6753462603878116,
88
+ 0.6803324099722993,
89
+ 0.679432132963989,
90
+ 0.681994459833795,
91
+ 0.6783240997229918,
92
+ 0.6828947368421053,
93
+ 0.6878808864265928,
94
+ 0.682202216066482,
95
+ 0.6894044321329641,
96
+ 0.6873268698060943,
97
+ 0.6961911357340721,
98
+ 0.6887811634349031,
99
+ 0.6945290858725762,
100
+ 0.6951523545706372,
101
+ 0.6924515235457064,
102
+ 0.6857340720221607,
103
+ 0.6923130193905818,
104
+ 0.6969529085872577,
105
+ 0.6984072022160666,
106
+ 0.6939750692520776,
107
+ 0.6948753462603878,
108
+ 0.6914819944598338,
109
+ 0.7003462603878117,
110
+ 0.7146814404432134,
111
+ 0.7172437673130194,
112
+ 0.7163434903047092,
113
+ 0.718213296398892,
114
+ 0.726038781163435,
115
+ 0.7243074792243768,
116
+ 0.7211218836565098,
117
+ 0.7233379501385042,
118
+ 0.7211218836565098,
119
+ 0.7306786703601109,
120
+ 0.7335180055401662,
121
+ 0.7299861495844876,
122
+ 0.7316481994459835,
123
+ 0.7347645429362881,
124
+ 0.7342797783933519,
125
+ 0.7364958448753464,
126
+ 0.7401662049861496,
127
+ 0.7405124653739613,
128
+ 0.7437673130193907,
129
+ 0.739819944598338
130
+ ],
131
+ "val_loss": [
132
+ 0.7741910591215021,
133
+ 0.6627067341054671,
134
+ 0.6433695114403722,
135
+ 0.5698822019709164,
136
+ 0.5478656911645441,
137
+ 0.5843131156193859,
138
+ 0.5847545998426038,
139
+ 0.5119658482329442,
140
+ 0.4913845322700564,
141
+ 0.5155180228512894,
142
+ 0.4377675685306783,
143
+ 0.545874192939294,
144
+ 0.4948514838093652,
145
+ 0.5456388587461933,
146
+ 0.48923359883182754,
147
+ 0.470527210192952,
148
+ 0.4415759726919839,
149
+ 0.4439484859002991,
150
+ 0.45745462403979725,
151
+ 0.4670618483166588,
152
+ 0.43405280420215975,
153
+ 0.4686297321008918,
154
+ 0.5249237610601847,
155
+ 0.4839318964368666,
156
+ 0.4510905831454062,
157
+ 0.49603641218412037,
158
+ 0.43269116203060964,
159
+ 0.47405282962511336,
160
+ 0.4512257693234714,
161
+ 0.43939952025061557,
162
+ 0.41432643819005793,
163
+ 0.4558428285258146,
164
+ 0.40343399825133563,
165
+ 0.4311320897000011,
166
+ 0.4005066976867744,
167
+ 0.3886150384376681,
168
+ 0.40475053726835675,
169
+ 0.40963226624795557,
170
+ 0.36675084849012135,
171
+ 0.37128675532460637,
172
+ 0.3558738076376197,
173
+ 0.35085392606107885,
174
+ 0.37169393350503793,
175
+ 0.3381592251816279,
176
+ 0.36775484197287933,
177
+ 0.34844720155222586,
178
+ 0.3504975924853871,
179
+ 0.36934267174043717,
180
+ 0.3295419710320769,
181
+ 0.32782930966409984
182
+ ],
183
+ "val_acc": [
184
+ 0.7487860148915507,
185
+ 0.78666235027517,
186
+ 0.7856911621884105,
187
+ 0.8290708967303335,
188
+ 0.8271285205568145,
189
+ 0.8125606992554225,
190
+ 0.813531887342182,
191
+ 0.8303658141793461,
192
+ 0.838459048235675,
193
+ 0.8429912593072192,
194
+ 0.8627387504046617,
195
+ 0.828099708643574,
196
+ 0.8523794108125607,
197
+ 0.8174166396892198,
198
+ 0.8552929750728392,
199
+ 0.8546455163483329,
200
+ 0.8679184202007122,
201
+ 0.8601489155066365,
202
+ 0.8598251861443833,
203
+ 0.8620912916801554,
204
+ 0.8808675946908385,
205
+ 0.8582065393331175,
206
+ 0.8319844609906119,
207
+ 0.8481709291032697,
208
+ 0.8588539980576239,
209
+ 0.8410488831337003,
210
+ 0.8685658789252185,
211
+ 0.8514082227258013,
212
+ 0.856911621884105,
213
+ 0.8617675623179023,
214
+ 0.8750404661702816,
215
+ 0.8582065393331175,
216
+ 0.875687924894788,
217
+ 0.8640336678536744,
218
+ 0.8721269019100033,
219
+ 0.8760116542570412,
220
+ 0.8747167368080285,
221
+ 0.8740692780835222,
222
+ 0.8808675946908385,
223
+ 0.8876659112981548,
224
+ 0.8896082874716738,
225
+ 0.8967303334412432,
226
+ 0.877630301068307,
227
+ 0.8941404985432179,
228
+ 0.8918743930074459,
229
+ 0.8902557461961801,
230
+ 0.8934930398187116,
231
+ 0.8802201359663322,
232
+ 0.9006150857882811,
233
+ 0.8986727096147621
234
+ ],
235
+ "lr": [
236
+ 0.001,
237
+ 0.001,
238
+ 0.001,
239
+ 0.001,
240
+ 0.001,
241
+ 0.001,
242
+ 0.001,
243
+ 0.001,
244
+ 0.001,
245
+ 0.001,
246
+ 0.001,
247
+ 0.001,
248
+ 0.001,
249
+ 0.001,
250
+ 0.001,
251
+ 0.001,
252
+ 0.001,
253
+ 0.001,
254
+ 0.001,
255
+ 0.001,
256
+ 0.001,
257
+ 0.001,
258
+ 0.001,
259
+ 0.001,
260
+ 0.001,
261
+ 0.001,
262
+ 0.001,
263
+ 0.001,
264
+ 0.0005,
265
+ 0.0005,
266
+ 0.0005,
267
+ 0.0005,
268
+ 0.0005,
269
+ 0.0005,
270
+ 0.0005,
271
+ 0.0005,
272
+ 0.00025,
273
+ 0.00025,
274
+ 0.00025,
275
+ 0.00025,
276
+ 0.00025,
277
+ 0.00025,
278
+ 0.00025,
279
+ 0.00025,
280
+ 0.00025,
281
+ 0.00025,
282
+ 0.00025,
283
+ 0.00025,
284
+ 0.00025,
285
+ 0.00025
286
+ ]
287
+ },
288
+ "model_architecture": "ResNet50",
289
+ "state_dict_keys": 334,
290
+ "total_parameters": 26198022,
291
+ "detected_num_classes": 15,
292
+ "training_analysis": {
293
+ "train_loss": {
294
+ "epochs": 50,
295
+ "final_value": 0.7982220532986596,
296
+ "best_value": 0.7915693512336038
297
+ },
298
+ "val_loss": {
299
+ "epochs": 50,
300
+ "final_value": 0.32782930966409984,
301
+ "best_value": 0.32782930966409984
302
+ },
303
+ "train_acc": {
304
+ "epochs": 50,
305
+ "final_value": 0.739819944598338,
306
+ "best_value": 0.7437673130193907
307
+ },
308
+ "val_acc": {
309
+ "epochs": 50,
310
+ "final_value": 0.8986727096147621,
311
+ "best_value": 0.9006150857882811
312
+ }
313
+ }
314
+ },
315
+ "model_comparison": {
316
+ "V0": {
317
+ "file_size_mb": 94.01632976531982,
318
+ "format": "full_checkpoint",
319
+ "num_classes": "Unknown",
320
+ "best_val_acc": "Unknown",
321
+ "class_names": 17
322
+ },
323
+ "V2": {
324
+ "file_size_mb": 100.05187511444092,
325
+ "format": "full_checkpoint",
326
+ "num_classes": "Unknown",
327
+ "best_val_acc": "Unknown",
328
+ "class_names": 17,
329
+ "final_val_accuracy": 0.11764705882352941,
330
+ "peak_val_accuracy": 0.11764705882352941,
331
+ "training_epochs": 20
332
+ },
333
+ "V3": {
334
+ "file_size_mb": 100.05542659759521,
335
+ "format": "full_checkpoint",
336
+ "num_classes": 15,
337
+ "best_val_acc": 0.9006150857882811,
338
+ "class_names": 15,
339
+ "final_val_accuracy": 0.8986727096147621,
340
+ "peak_val_accuracy": 0.9006150857882811,
341
+ "training_epochs": 50
342
+ }
343
+ },
344
+ "analysis_timestamp": "2025-09-13T12:19:43.789108"
345
+ }
outputs/v3_evaluation_report.json ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "evaluation_timestamp": "2025-09-13T12:17:30.853732",
3
+ "model_comparison": {
4
+ "V0": {
5
+ "file_size_mb": 94.01632976531982,
6
+ "parameters": 0,
7
+ "state_dict_keys": 3,
8
+ "conv_layers": 0,
9
+ "batch_norm_layers": 0,
10
+ "fc_layers": 0
11
+ },
12
+ "V2": {
13
+ "file_size_mb": 100.05187511444092,
14
+ "parameters": 0,
15
+ "state_dict_keys": 6,
16
+ "conv_layers": 0,
17
+ "batch_norm_layers": 0,
18
+ "fc_layers": 0
19
+ },
20
+ "V3": {
21
+ "file_size_mb": 100.05542659759521,
22
+ "parameters": 0,
23
+ "state_dict_keys": 7,
24
+ "conv_layers": 0,
25
+ "batch_norm_layers": 0,
26
+ "fc_layers": 0
27
+ }
28
+ },
29
+ "v3_evaluation": {
30
+ "timestamp": "2025-09-13T12:17:29.981759",
31
+ "model_version": "V3",
32
+ "status": "evaluation_attempted",
33
+ "error": "Failed to load V3 model"
34
+ }
35
+ }
requirements.txt ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FastAPI App Dependencies - Hugging Face Spaces
2
+ # Optimized for crop disease detection RESTful API deployment
3
+
4
+ # Core ML framework
5
+ torch
6
+ torchvision
7
+
8
+ # FastAPI framework
9
+ fastapi
10
+ uvicorn[standard]
11
+ python-multipart
12
+ # Image processing
13
+ Pillow
14
+ opencv-python-headless
15
+ # Grad-CAM visualization
16
+ grad-cam
17
+
18
+ # Data visualization
19
+ matplotlib>=3.7.0
20
+
21
+ # Utilities
22
+ requests>=2.31.0
23
+ tqdm>=4.65.0
24
+
25
+ # JSON handling and validation
26
+ pydantic>=2.0.0
src/__init__.py ADDED
File without changes
src/dataset.py ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Dataset loading and preprocessing for crop disease detection
3
+ """
4
+
5
+ import os
6
+ import torch
7
+ from torch.utils.data import Dataset, DataLoader
8
+ from torchvision import transforms
9
+ from PIL import Image
10
+ import numpy as np
11
+ from pathlib import Path
12
+
13
+ def get_transforms(split='train', input_size=224):
14
+ """
15
+ Get image transforms for different dataset splits
16
+
17
+ Args:
18
+ split: 'train', 'val', or 'test'
19
+ input_size: Input image size (default: 224)
20
+
21
+ Returns:
22
+ transforms.Compose: Composed transforms
23
+ """
24
+ if split == 'train':
25
+ # Training transforms with augmentation
26
+ return transforms.Compose([
27
+ transforms.Resize((input_size, input_size)),
28
+ transforms.RandomHorizontalFlip(p=0.5),
29
+ transforms.RandomVerticalFlip(p=0.3),
30
+ transforms.RandomRotation(degrees=15),
31
+ transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
32
+ transforms.ToTensor(),
33
+ transforms.Normalize(mean=[0.485, 0.456, 0.406],
34
+ std=[0.229, 0.224, 0.225])
35
+ ])
36
+ else:
37
+ # Validation/test transforms (no augmentation)
38
+ return transforms.Compose([
39
+ transforms.Resize((input_size, input_size)),
40
+ transforms.ToTensor(),
41
+ transforms.Normalize(mean=[0.485, 0.456, 0.406],
42
+ std=[0.229, 0.224, 0.225])
43
+ ])
44
+
45
+ def get_inference_transforms(input_size=224):
46
+ """
47
+ Get transforms for inference (prediction)
48
+
49
+ Args:
50
+ input_size: Input image size (default: 224)
51
+
52
+ Returns:
53
+ transforms.Compose: Composed transforms for inference
54
+ """
55
+ return transforms.Compose([
56
+ transforms.Resize((input_size, input_size)),
57
+ transforms.ToTensor(),
58
+ transforms.Normalize(mean=[0.485, 0.456, 0.406],
59
+ std=[0.229, 0.224, 0.225])
60
+ ])
61
+
62
+ class CropDiseaseDataset(Dataset):
63
+ """Custom dataset for crop disease images"""
64
+
65
+ def __init__(self, data_dir, transform=None, class_to_idx=None):
66
+ """
67
+ Args:
68
+ data_dir: Path to dataset directory (train/val/test)
69
+ transform: Optional transform to be applied on images
70
+ class_to_idx: Dictionary mapping class names to indices
71
+ """
72
+ self.data_dir = Path(data_dir)
73
+ self.transform = transform
74
+
75
+ # Get all image files and their labels
76
+ self.samples = []
77
+ self.classes = []
78
+
79
+ # Scan all class directories
80
+ for class_dir in sorted(self.data_dir.iterdir()):
81
+ if class_dir.is_dir() and not class_dir.name.startswith('.'):
82
+ self.classes.append(class_dir.name)
83
+
84
+ # Create class to index mapping if not provided
85
+ if class_to_idx is None:
86
+ self.class_to_idx = {cls_name: idx for idx, cls_name in enumerate(self.classes)}
87
+ else:
88
+ self.class_to_idx = class_to_idx
89
+
90
+ # Collect all image samples
91
+ image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif'}
92
+
93
+ for class_name in self.classes:
94
+ class_dir = self.data_dir / class_name
95
+ class_idx = self.class_to_idx[class_name]
96
+
97
+ for img_path in class_dir.iterdir():
98
+ if img_path.suffix.lower() in image_extensions:
99
+ self.samples.append((str(img_path), class_idx))
100
+
101
+ def __len__(self):
102
+ return len(self.samples)
103
+
104
+ def __getitem__(self, idx):
105
+ img_path, label = self.samples[idx]
106
+
107
+ # Load image
108
+ try:
109
+ image = Image.open(img_path).convert('RGB')
110
+ except Exception as e:
111
+ # Create a dummy image if file doesn't exist or is corrupted
112
+ print(f"Warning: Could not load {img_path}, creating dummy image")
113
+ image = Image.new('RGB', (224, 224), color=(128, 128, 128))
114
+
115
+ # Apply transforms
116
+ if self.transform:
117
+ image = self.transform(image)
118
+
119
+ return image, label
120
+
121
+ def get_class_names(self):
122
+ """Return list of class names"""
123
+ return self.classes
124
+
125
+ def get_class_to_idx(self):
126
+ """Return class to index mapping"""
127
+ return self.class_to_idx
128
+
129
+ def get_data_transforms():
130
+ """Get data transforms for training and validation"""
131
+
132
+ # ImageNet normalization values
133
+ normalize = transforms.Normalize(
134
+ mean=[0.485, 0.456, 0.406],
135
+ std=[0.229, 0.224, 0.225]
136
+ )
137
+
138
+ # Training transforms with data augmentation
139
+ train_transforms = transforms.Compose([
140
+ transforms.Resize((256, 256)),
141
+ transforms.RandomResizedCrop(224),
142
+ transforms.RandomHorizontalFlip(p=0.5),
143
+ transforms.RandomVerticalFlip(p=0.3),
144
+ transforms.RandomRotation(degrees=15),
145
+ transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
146
+ transforms.ToTensor(),
147
+ normalize
148
+ ])
149
+
150
+ # Validation/Test transforms (no augmentation)
151
+ val_transforms = transforms.Compose([
152
+ transforms.Resize((224, 224)),
153
+ transforms.ToTensor(),
154
+ normalize
155
+ ])
156
+
157
+ return train_transforms, val_transforms
158
+
159
+ def create_data_loaders(data_dir, batch_size=32, num_workers=0):
160
+ """Create data loaders for training, validation, and testing"""
161
+
162
+ train_transforms, val_transforms = get_data_transforms()
163
+ pin_memory = torch.cuda.is_available()
164
+
165
+ # Create datasets
166
+ train_dataset = CropDiseaseDataset(
167
+ data_dir=os.path.join(data_dir, 'train'),
168
+ transform=train_transforms
169
+ )
170
+
171
+ val_dataset = CropDiseaseDataset(
172
+ data_dir=os.path.join(data_dir, 'val'),
173
+ transform=val_transforms,
174
+ class_to_idx=train_dataset.get_class_to_idx()
175
+ )
176
+
177
+ test_dataset = CropDiseaseDataset(
178
+ data_dir=os.path.join(data_dir, 'test'),
179
+ transform=val_transforms,
180
+ class_to_idx=train_dataset.get_class_to_idx()
181
+ )
182
+
183
+ # Create data loaders
184
+ train_loader = DataLoader(
185
+ train_dataset,
186
+ batch_size=batch_size,
187
+ shuffle=True,
188
+ num_workers=num_workers,
189
+ pin_memory=pin_memory
190
+ )
191
+
192
+ val_loader = DataLoader(
193
+ val_dataset,
194
+ batch_size=batch_size,
195
+ shuffle=False,
196
+ num_workers=num_workers,
197
+ pin_memory=pin_memory
198
+ )
199
+
200
+ test_loader = DataLoader(
201
+ test_dataset,
202
+ batch_size=batch_size,
203
+ shuffle=False,
204
+ num_workers=num_workers,
205
+ pin_memory=pin_memory
206
+ )
207
+
208
+ return train_loader, val_loader, test_loader, train_dataset.get_class_names()
209
+
210
+ def get_class_weights(data_dir):
211
+ """Calculate class weights for handling imbalanced datasets"""
212
+
213
+ train_dataset = CropDiseaseDataset(data_dir=os.path.join(data_dir, 'train'))
214
+
215
+ # Count samples per class
216
+ class_counts = {}
217
+ for _, label in train_dataset.samples:
218
+ class_name = train_dataset.classes[label]
219
+ class_counts[class_name] = class_counts.get(class_name, 0) + 1
220
+
221
+ # Calculate weights (inverse frequency)
222
+ total_samples = len(train_dataset.samples)
223
+ num_classes = len(train_dataset.classes)
224
+
225
+ class_weights = []
226
+ for class_name in train_dataset.classes:
227
+ count = class_counts.get(class_name, 1)
228
+ weight = total_samples / (num_classes * count)
229
+ class_weights.append(weight)
230
+
231
+ return torch.FloatTensor(class_weights)
232
+
233
+ if __name__ == "__main__":
234
+ # Test the dataset loading
235
+ data_dir = "data"
236
+
237
+ try:
238
+ train_loader, val_loader, test_loader, class_names = create_data_loaders(data_dir, batch_size=4)
239
+
240
+ print(f"Dataset loaded successfully!")
241
+ print(f"Number of classes: {len(class_names)}")
242
+ print(f"Classes: {class_names}")
243
+ print(f"Train batches: {len(train_loader)}")
244
+ print(f"Val batches: {len(val_loader)}")
245
+ print(f"Test batches: {len(test_loader)}")
246
+
247
+ # Test loading a batch
248
+ for images, labels in train_loader:
249
+ print(f"Batch shape: {images.shape}")
250
+ print(f"Label shape: {labels.shape}")
251
+ break
252
+
253
+ except Exception as e:
254
+ print(f"Error loading dataset: {e}")
255
+ print("Make sure the dataset is properly organized in data/train, data/val, data/test")
src/evaluate.py ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Model evaluation script for crop disease detection
3
+ """
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import numpy as np
8
+ import json
9
+ from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
10
+ from sklearn.metrics import precision_recall_fscore_support
11
+ import matplotlib.pyplot as plt
12
+ import seaborn as sns
13
+ from pathlib import Path
14
+
15
+ from .dataset import create_data_loaders
16
+ from .model import create_model, load_checkpoint
17
+
18
+ class ModelEvaluator:
19
+ """Evaluate trained model performance"""
20
+
21
+ def __init__(self, model, test_loader, class_names, device='cpu'):
22
+ self.model = model
23
+ self.test_loader = test_loader
24
+ self.class_names = class_names
25
+ self.device = device
26
+
27
+ def evaluate(self):
28
+ """Evaluate model on test dataset"""
29
+ self.model.eval()
30
+
31
+ all_preds = []
32
+ all_labels = []
33
+ all_probs = []
34
+
35
+ with torch.no_grad():
36
+ for inputs, labels in self.test_loader:
37
+ inputs = inputs.to(self.device)
38
+ labels = labels.to(self.device)
39
+
40
+ outputs = self.model(inputs)
41
+ probs = torch.softmax(outputs, dim=1)
42
+ _, preds = torch.max(outputs, 1)
43
+
44
+ all_preds.extend(preds.cpu().numpy())
45
+ all_labels.extend(labels.cpu().numpy())
46
+ all_probs.extend(probs.cpu().numpy())
47
+
48
+ return np.array(all_preds), np.array(all_labels), np.array(all_probs)
49
+
50
+ def calculate_metrics(self, y_true, y_pred, y_probs):
51
+ """Calculate comprehensive evaluation metrics"""
52
+
53
+ # Basic metrics
54
+ accuracy = accuracy_score(y_true, y_pred)
55
+
56
+ # Per-class metrics
57
+ precision, recall, f1, support = precision_recall_fscore_support(
58
+ y_true, y_pred, average=None, labels=range(len(self.class_names))
59
+ )
60
+
61
+ # Macro and weighted averages
62
+ precision_macro, recall_macro, f1_macro, _ = precision_recall_fscore_support(
63
+ y_true, y_pred, average='macro'
64
+ )
65
+
66
+ precision_weighted, recall_weighted, f1_weighted, _ = precision_recall_fscore_support(
67
+ y_true, y_pred, average='weighted'
68
+ )
69
+
70
+ # Confusion matrix
71
+ cm = confusion_matrix(y_true, y_pred)
72
+
73
+ # Classification report
74
+ class_report = classification_report(
75
+ y_true, y_pred,
76
+ target_names=self.class_names,
77
+ output_dict=True
78
+ )
79
+
80
+ metrics = {
81
+ 'accuracy': float(accuracy),
82
+ 'precision_macro': float(precision_macro),
83
+ 'recall_macro': float(recall_macro),
84
+ 'f1_macro': float(f1_macro),
85
+ 'precision_weighted': float(precision_weighted),
86
+ 'recall_weighted': float(recall_weighted),
87
+ 'f1_weighted': float(f1_weighted),
88
+ 'per_class_metrics': {
89
+ 'precision': precision.tolist(),
90
+ 'recall': recall.tolist(),
91
+ 'f1_score': f1.tolist(),
92
+ 'support': support.tolist()
93
+ },
94
+ 'confusion_matrix': cm.tolist(),
95
+ 'classification_report': class_report
96
+ }
97
+
98
+ return metrics
99
+
100
+ def plot_confusion_matrix(self, cm, save_path='outputs/confusion_matrix.png'):
101
+ """Plot and save confusion matrix"""
102
+ plt.figure(figsize=(12, 10))
103
+
104
+ # Normalize confusion matrix
105
+ cm_normalized = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
106
+
107
+ # Create heatmap
108
+ sns.heatmap(
109
+ cm_normalized,
110
+ annot=True,
111
+ fmt='.2f',
112
+ cmap='Blues',
113
+ xticklabels=[name.replace('___', '\n') for name in self.class_names],
114
+ yticklabels=[name.replace('___', '\n') for name in self.class_names],
115
+ cbar_kws={'label': 'Normalized Frequency'}
116
+ )
117
+
118
+ plt.title('Confusion Matrix (Normalized)', fontsize=16, pad=20)
119
+ plt.xlabel('Predicted Label', fontsize=12)
120
+ plt.ylabel('True Label', fontsize=12)
121
+ plt.xticks(rotation=45, ha='right')
122
+ plt.yticks(rotation=0)
123
+ plt.tight_layout()
124
+
125
+ # Save plot
126
+ Path(save_path).parent.mkdir(parents=True, exist_ok=True)
127
+ plt.savefig(save_path, dpi=300, bbox_inches='tight')
128
+ plt.close()
129
+
130
+ print(f"Confusion matrix saved to: {save_path}")
131
+
132
+ def plot_per_class_metrics(self, metrics, save_path='outputs/per_class_metrics.png'):
133
+ """Plot per-class performance metrics"""
134
+ fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18, 6))
135
+
136
+ class_names_short = [name.replace('___', '\n') for name in self.class_names]
137
+ x_pos = np.arange(len(self.class_names))
138
+
139
+ # Precision
140
+ ax1.bar(x_pos, metrics['per_class_metrics']['precision'], color='skyblue', alpha=0.7)
141
+ ax1.set_title('Precision per Class')
142
+ ax1.set_ylabel('Precision')
143
+ ax1.set_xticks(x_pos)
144
+ ax1.set_xticklabels(class_names_short, rotation=45, ha='right')
145
+ ax1.set_ylim(0, 1)
146
+ ax1.grid(True, alpha=0.3)
147
+
148
+ # Recall
149
+ ax2.bar(x_pos, metrics['per_class_metrics']['recall'], color='lightcoral', alpha=0.7)
150
+ ax2.set_title('Recall per Class')
151
+ ax2.set_ylabel('Recall')
152
+ ax2.set_xticks(x_pos)
153
+ ax2.set_xticklabels(class_names_short, rotation=45, ha='right')
154
+ ax2.set_ylim(0, 1)
155
+ ax2.grid(True, alpha=0.3)
156
+
157
+ # F1-Score
158
+ ax3.bar(x_pos, metrics['per_class_metrics']['f1_score'], color='lightgreen', alpha=0.7)
159
+ ax3.set_title('F1-Score per Class')
160
+ ax3.set_ylabel('F1-Score')
161
+ ax3.set_xticks(x_pos)
162
+ ax3.set_xticklabels(class_names_short, rotation=45, ha='right')
163
+ ax3.set_ylim(0, 1)
164
+ ax3.grid(True, alpha=0.3)
165
+
166
+ plt.tight_layout()
167
+ plt.savefig(save_path, dpi=300, bbox_inches='tight')
168
+ plt.close()
169
+
170
+ print(f"Per-class metrics plot saved to: {save_path}")
171
+
172
+ def save_results(self, metrics, save_path='outputs/results.json'):
173
+ """Save evaluation results to JSON file"""
174
+
175
+ # Add class names to results
176
+ results = {
177
+ 'class_names': self.class_names,
178
+ 'num_classes': len(self.class_names),
179
+ 'test_samples': len(self.test_loader.dataset),
180
+ 'metrics': metrics,
181
+ 'model_info': {
182
+ 'architecture': 'ResNet50',
183
+ 'pretrained': True,
184
+ 'transfer_learning': True
185
+ }
186
+ }
187
+
188
+ # Save to file
189
+ Path(save_path).parent.mkdir(parents=True, exist_ok=True)
190
+ with open(save_path, 'w') as f:
191
+ json.dump(results, f, indent=2)
192
+
193
+ print(f"Results saved to: {save_path}")
194
+
195
+ return results
196
+
197
+ def print_summary(self, metrics):
198
+ """Print evaluation summary"""
199
+ print("\n" + "="*60)
200
+ print("MODEL EVALUATION SUMMARY")
201
+ print("="*60)
202
+ print(f"Test Accuracy: {metrics['accuracy']:.4f}")
203
+ print(f"Precision (Macro): {metrics['precision_macro']:.4f}")
204
+ print(f"Recall (Macro): {metrics['recall_macro']:.4f}")
205
+ print(f"F1-Score (Macro): {metrics['f1_macro']:.4f}")
206
+ print(f"F1-Score (Weighted): {metrics['f1_weighted']:.4f}")
207
+ print("\nPer-Class Performance:")
208
+ print("-" * 60)
209
+
210
+ for i, class_name in enumerate(self.class_names):
211
+ precision = metrics['per_class_metrics']['precision'][i]
212
+ recall = metrics['per_class_metrics']['recall'][i]
213
+ # Fixed key typo: per_class_metvrics -> per_class_metrics
214
+ f1 = metrics['per_class_metrics']['f1_score'][i]
215
+ support = metrics['per_class_metrics']['support'][i]
216
+
217
+ print(f"{class_name:40} | P: {precision:.3f} | R: {recall:.3f} | F1: {f1:.3f} | N: {support:2d}")
218
+
219
+ print("="*60)
220
+
221
+ def evaluate_model(checkpoint_path, data_dir='data', batch_size=32):
222
+ """Main evaluation function"""
223
+
224
+ # Device setup
225
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
226
+ print(f"Using device: {device}")
227
+
228
+ # Load data
229
+ print("Loading test dataset...")
230
+ _, _, test_loader, class_names = create_data_loaders(
231
+ data_dir=data_dir,
232
+ batch_size=batch_size,
233
+ num_workers=0
234
+ )
235
+
236
+ print(f"Test dataset loaded: {len(test_loader.dataset)} samples")
237
+
238
+ # Create and load model
239
+ print("Loading trained model...")
240
+ model = create_model(num_classes=len(class_names), device=device)
241
+
242
+ try:
243
+ model, _, epoch, _ = load_checkpoint(checkpoint_path, model, device=device)
244
+ print(f"Model loaded successfully from epoch {epoch}")
245
+ except Exception as e:
246
+ print(f"Error loading checkpoint: {e}")
247
+ print("Using untrained model for testing...")
248
+
249
+ # Create evaluator
250
+ evaluator = ModelEvaluator(model, test_loader, class_names, device)
251
+
252
+ # Run evaluation
253
+ print("Evaluating model...")
254
+ y_pred, y_true, y_probs = evaluator.evaluate()
255
+
256
+ # Calculate metrics
257
+ metrics = evaluator.calculate_metrics(y_true, y_pred, y_probs)
258
+
259
+ # Print summary
260
+ evaluator.print_summary(metrics)
261
+
262
+ # Generate plots
263
+ evaluator.plot_confusion_matrix(metrics['confusion_matrix'])
264
+ evaluator.plot_per_class_metrics(metrics)
265
+
266
+ # Save results
267
+ results = evaluator.save_results(metrics)
268
+
269
+ return results
270
+
271
+ if __name__ == "__main__":
272
+ # Evaluate the trained model
273
+ results = evaluate_model(
274
+ # Use an existing default checkpoint file name
275
+ checkpoint_path='models/crop_disease_v3_model.pth',
276
+ data_dir='data',
277
+ batch_size=16
278
+ )
src/explain.py ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Grad-CAM Implementation for Crop Disease Detection using pytorch-grad-cam
3
+ Generates visual explanations showing which parts of the leaf image the model focuses on
4
+ """
5
+
6
+ import torch
7
+ import torch.nn.functional as F
8
+ import cv2
9
+ import numpy as np
10
+ from PIL import Image
11
+ import matplotlib.pyplot as plt
12
+ import matplotlib.cm as cm
13
+ from pathlib import Path
14
+ import base64
15
+ import io
16
+ import os
17
+
18
+ try:
19
+ from pytorch_grad_cam import GradCAM
20
+ from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
21
+ from pytorch_grad_cam.utils.image import show_cam_on_image, preprocess_image
22
+ PYTORCH_GRAD_CAM_AVAILABLE = True
23
+ except ImportError:
24
+ print("Warning: pytorch-grad-cam not available. Installing...")
25
+ import subprocess
26
+ import sys
27
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "grad-cam"])
28
+ try:
29
+ from pytorch_grad_cam import GradCAM
30
+ from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
31
+ from pytorch_grad_cam.utils.image import show_cam_on_image, preprocess_image
32
+ PYTORCH_GRAD_CAM_AVAILABLE = True
33
+ except ImportError:
34
+ PYTORCH_GRAD_CAM_AVAILABLE = False
35
+ print("Warning: Could not import pytorch-grad-cam after installation")
36
+
37
+ class CropDiseaseExplainer:
38
+ """High-level interface for crop disease explanation using pytorch-grad-cam"""
39
+
40
+ def __init__(self, model, class_names, device='cpu'):
41
+ """
42
+ Initialize explainer
43
+
44
+ Args:
45
+ model: Trained model
46
+ class_names: List of class names
47
+ device: Device to run on
48
+ """
49
+ self.model = model.to(device)
50
+ self.class_names = class_names
51
+ self.device = device
52
+
53
+ # Define target layer for Grad-CAM (last convolutional layer)
54
+ target_layers = []
55
+ # Try different model architectures
56
+ if hasattr(model, 'resnet') and hasattr(model.resnet, 'layer4'):
57
+ # For our CropDiseaseResNet50 model
58
+ target_layers = [model.resnet.layer4[-1]]
59
+ print(f"Using target layer: model.resnet.layer4[-1]")
60
+ elif hasattr(model, 'layer4'):
61
+ # For standard ResNet
62
+ target_layers = [model.layer4[-1]]
63
+ print(f"Using target layer: model.layer4[-1]")
64
+ else:
65
+ # Try to find the last convolutional layer
66
+ for name, module in model.named_modules():
67
+ if isinstance(module, (torch.nn.Conv2d, torch.nn.modules.conv.Conv2d)):
68
+ target_layers = [module]
69
+ print(f"Using target layer: {name}")
70
+
71
+ if not target_layers:
72
+ print("Warning: Could not find suitable target layer for Grad-CAM")
73
+ self.grad_cam = None
74
+ return
75
+
76
+ self.target_layers = target_layers
77
+
78
+ # Initialize Grad-CAM
79
+ if PYTORCH_GRAD_CAM_AVAILABLE:
80
+ try:
81
+ self.grad_cam = GradCAM(model=self.model, target_layers=self.target_layers)
82
+ print("✅ Grad-CAM initialized successfully")
83
+ except Exception as e:
84
+ print(f"Error initializing Grad-CAM: {e}")
85
+ self.grad_cam = None
86
+ else:
87
+ self.grad_cam = None
88
+ print("Warning: pytorch-grad-cam not available, Grad-CAM disabled")
89
+
90
+ def explain_prediction(self, image_path, save_dir='outputs/heatmaps',
91
+ return_base64=False, target_class=None):
92
+ """
93
+ Generate complete explanation for an image
94
+
95
+ Args:
96
+ image_path: Path to input image
97
+ save_dir: Directory to save explanations
98
+ return_base64: Whether to return base64 encoded image
99
+ target_class: Specific class to target (if None, uses predicted class)
100
+
101
+ Returns:
102
+ explanation: Dictionary with prediction and explanation
103
+ """
104
+ if not PYTORCH_GRAD_CAM_AVAILABLE or self.grad_cam is None:
105
+ return {'error': 'Grad-CAM not available'}
106
+
107
+ # Load and preprocess image
108
+ original_image = Image.open(image_path).convert('RGB')
109
+ original_np = np.array(original_image) / 255.0 # Normalize to [0,1]
110
+
111
+ # Preprocessing transforms (should match training transforms)
112
+ from torchvision import transforms
113
+ transform = transforms.Compose([
114
+ transforms.Resize((224, 224)),
115
+ transforms.ToTensor(),
116
+ transforms.Normalize(mean=[0.485, 0.456, 0.406],
117
+ std=[0.229, 0.224, 0.225])
118
+ ])
119
+
120
+ input_tensor = transform(original_image).unsqueeze(0).to(self.device)
121
+
122
+ # Get prediction
123
+ self.model.eval()
124
+ with torch.no_grad():
125
+ outputs = self.model(input_tensor)
126
+ probabilities = F.softmax(outputs, dim=1)
127
+ predicted_idx = torch.argmax(probabilities, dim=1).item()
128
+ confidence = probabilities[0][predicted_idx].item()
129
+
130
+ # Use target class if specified, otherwise use predicted class
131
+ target_idx = target_class if target_class is not None else predicted_idx
132
+ targets = [ClassifierOutputTarget(target_idx)]
133
+
134
+ # Generate Grad-CAM
135
+ try:
136
+ # Resize original image for overlay
137
+ original_resized = cv2.resize(np.array(original_image), (224, 224))
138
+ original_resized = original_resized / 255.0
139
+
140
+ print(f"Input tensor shape: {input_tensor.shape}")
141
+ print(f"Targets: {targets}")
142
+
143
+ # Generate CAM
144
+ # First attempt with default target layer
145
+ grayscale_cam = self.grad_cam(input_tensor=input_tensor, targets=targets)
146
+ # Validate CAM result before accessing attributes
147
+ if grayscale_cam is None:
148
+ print("Grad-CAM returned None")
149
+ # Try a fallback target layer if available (e.g., last conv inside bottleneck)
150
+ fallback_cam = self._try_fallback_cam(input_tensor, targets)
151
+ if fallback_cam is None:
152
+ return {'error': 'Failed to generate Grad-CAM heatmap'}
153
+ grayscale_cam = fallback_cam
154
+ # Ensure numpy array
155
+ if isinstance(grayscale_cam, torch.Tensor):
156
+ grayscale_cam = grayscale_cam.detach().cpu().numpy()
157
+ # Basic sanity checks
158
+ try:
159
+ _ = grayscale_cam.shape
160
+ except Exception:
161
+ print("Grad-CAM result has no shape attribute")
162
+ return {'error': 'Invalid Grad-CAM heatmap shape'}
163
+ print(f"Generated CAM type: {type(grayscale_cam)}")
164
+ print(f"Generated CAM shape: {grayscale_cam.shape}")
165
+
166
+ # Check if CAM was generated successfully
167
+ if grayscale_cam.size == 0:
168
+ # Try fallback if present
169
+ fallback_cam = self._try_fallback_cam(input_tensor, targets)
170
+ if fallback_cam is None or fallback_cam.size == 0:
171
+ return {'error': 'Failed to generate Grad-CAM heatmap'}
172
+ grayscale_cam = fallback_cam
173
+
174
+ grayscale_cam = grayscale_cam[0, :] # Take first (and only) image
175
+
176
+ # Create visualization
177
+ cam_image = show_cam_on_image(original_resized, grayscale_cam, use_rgb=True)
178
+
179
+ # Convert back to PIL Image
180
+ # Convert to PIL safely (avoid double scaling if already uint8)
181
+ if cam_image.dtype == np.uint8:
182
+ cam_pil = Image.fromarray(cam_image)
183
+ else:
184
+ cam_pil = Image.fromarray((np.clip(cam_image, 0, 1) * 255).astype(np.uint8))
185
+
186
+ # Create save directory
187
+ Path(save_dir).mkdir(parents=True, exist_ok=True)
188
+
189
+ # Save visualization
190
+ filename = Path(image_path).stem
191
+ save_path = Path(save_dir) / f"{filename}_gradcam.jpg"
192
+ cam_pil.save(save_path)
193
+
194
+ # Prepare return data
195
+ result = {
196
+ 'predicted_class': self.class_names[predicted_idx],
197
+ 'predicted_idx': predicted_idx,
198
+ 'confidence': confidence,
199
+ 'target_class': self.class_names[target_idx],
200
+ 'target_idx': target_idx,
201
+ 'save_path': str(save_path),
202
+ 'cam_image': cam_pil
203
+ }
204
+
205
+ # Add base64 encoding if requested
206
+ if return_base64:
207
+ buffer = io.BytesIO()
208
+ cam_pil.save(buffer, format='JPEG')
209
+ buffer.seek(0)
210
+ base64_str = base64.b64encode(buffer.getvalue()).decode()
211
+ result['overlay_base64'] = base64_str
212
+
213
+ return result
214
+
215
+ except Exception as e:
216
+ print(f"Error generating Grad-CAM: {e}")
217
+ return {'error': str(e)}
218
+
219
+ def _try_fallback_cam(self, input_tensor, targets):
220
+ """Try alternative target layers to compute CAM if the primary attempt fails."""
221
+ try:
222
+ # Determine a plausible fallback layer
223
+ fallback_layers = []
224
+ # If the target layer is a Bottleneck, try its last conv
225
+ try:
226
+ # For our wrapped model
227
+ if hasattr(self.model, 'resnet') and hasattr(self.model.resnet, 'layer4'):
228
+ bottleneck = self.model.resnet.layer4[-1]
229
+ if hasattr(bottleneck, 'conv3'):
230
+ fallback_layers = [bottleneck.conv3]
231
+ # For plain ResNet
232
+ elif hasattr(self.model, 'layer4'):
233
+ bottleneck = self.model.layer4[-1]
234
+ if hasattr(bottleneck, 'conv3'):
235
+ fallback_layers = [bottleneck.conv3]
236
+ except Exception:
237
+ pass
238
+ if not fallback_layers:
239
+ return None
240
+ print("Trying fallback Grad-CAM target layer (conv3 of last bottleneck)...")
241
+ from pytorch_grad_cam import GradCAM
242
+ cam = GradCAM(model=self.model, target_layers=fallback_layers)
243
+ grayscale_cam = cam(input_tensor=input_tensor, targets=targets)
244
+ if grayscale_cam is None:
245
+ return None
246
+ if isinstance(grayscale_cam, torch.Tensor):
247
+ grayscale_cam = grayscale_cam.detach().cpu().numpy()
248
+ return grayscale_cam
249
+ except Exception as _:
250
+ return None
251
+
252
+ def load_model_and_generate_gradcam(model_path, image_path, output_path=None, target_class=None):
253
+ """
254
+ Complete example function that loads a model and generates Grad-CAM visualization
255
+
256
+ Args:
257
+ model_path: Path to the saved model file
258
+ image_path: Path to input image
259
+ output_path: Path to save the output (optional)
260
+ target_class: Target class index (optional, uses prediction if None)
261
+
262
+ Returns:
263
+ Dictionary with results
264
+ """
265
+ # Import model
266
+ import sys
267
+ sys.path.append(os.path.join(os.path.dirname(__file__)))
268
+ from model import CropDiseaseResNet50
269
+
270
+ # Define class names
271
+ class_names = [
272
+ 'Corn___Cercospora_leaf_spot_Gray_leaf_spot',
273
+ 'Corn___Common_rust',
274
+ 'Corn___healthy',
275
+ 'Corn___Northern_Leaf_Blight',
276
+ 'Potato___Early_Blight',
277
+ 'Potato___healthy',
278
+ 'Potato___Late_Blight',
279
+ 'Tomato___Bacterial_spot',
280
+ 'Tomato___Early_blight',
281
+ 'Tomato___healthy',
282
+ 'Tomato___Late_blight',
283
+ 'Tomato___Leaf_Mold',
284
+ 'Tomato___Septoria_leaf_spot',
285
+ 'Tomato___Spider_mites_Two_spotted_spider_mite',
286
+ 'Tomato___Target_Spot',
287
+ 'Tomato___Tomato_mosaic_virus',
288
+ 'Tomato___Tomato_Yellow_Leaf_Curl_Virus'
289
+ ]
290
+
291
+ # Step 1: Load the trained model
292
+ print(f"Loading model from {model_path}...")
293
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
294
+
295
+ model = CropDiseaseResNet50(num_classes=len(class_names), pretrained=False)
296
+ checkpoint = torch.load(model_path, map_location=device)
297
+
298
+ # Handle checkpoint format
299
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
300
+ state_dict = checkpoint['model_state_dict']
301
+ if 'class_names' in checkpoint:
302
+ class_names = checkpoint['class_names']
303
+ else:
304
+ state_dict = checkpoint
305
+
306
+ model.load_state_dict(state_dict, strict=True)
307
+ model.to(device)
308
+ model.eval()
309
+ print(f"✅ Model loaded successfully!")
310
+
311
+ # Step 2: Initialize Grad-CAM explainer
312
+ print("Initializing Grad-CAM explainer...")
313
+ explainer = CropDiseaseExplainer(model, class_names, device)
314
+
315
+ # Step 3: Generate Grad-CAM visualization
316
+ print(f"Generating Grad-CAM for {image_path}...")
317
+ result = explainer.explain_prediction(
318
+ image_path=image_path,
319
+ save_dir='outputs/heatmaps',
320
+ return_base64=True,
321
+ target_class=target_class
322
+ )
323
+
324
+ if 'error' in result:
325
+ print(f"❌ Error: {result['error']}")
326
+ return result
327
+
328
+ # Step 4: Save output if path specified
329
+ if output_path:
330
+ result['cam_image'].save(output_path)
331
+ print(f"✅ Saved Grad-CAM visualization to {output_path}")
332
+
333
+ # Print results
334
+ print(f"✅ Grad-CAM generated successfully!")
335
+ print(f" Predicted: {result['predicted_class']} ({result['confidence']:.1%})")
336
+ print(f" Target: {result['target_class']}")
337
+ print(f" Saved to: {result['save_path']}")
338
+
339
+ return result
340
+
341
+ # Example usage
342
+ if __name__ == "__main__":
343
+ # Example usage
344
+ model_path = "../models/crop_disease_v3_model.pth"
345
+ image_path = "../test_leaf_sample.jpg"
346
+ output_path = "../outputs/gradcam_example.jpg"
347
+
348
+ if os.path.exists(model_path) and os.path.exists(image_path):
349
+ result = load_model_and_generate_gradcam(
350
+ model_path=model_path,
351
+ image_path=image_path,
352
+ output_path=output_path
353
+ )
354
+ else:
355
+ print("Model or image file not found!")
356
+ print(f"Model path: {model_path}")
357
+ print(f"Image path: {image_path}")
src/model.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ResNet50 model architecture for crop disease detection
3
+ """
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torchvision.models as models
8
+ from torchvision.models import ResNet50_Weights
9
+
10
+ class CropDiseaseResNet50(nn.Module):
11
+ """ResNet50 model for crop disease classification"""
12
+
13
+ def __init__(self, num_classes, pretrained=True, freeze_features=True):
14
+ """
15
+ Args:
16
+ num_classes: Number of disease classes
17
+ pretrained: Use ImageNet pretrained weights
18
+ freeze_features: Freeze feature extraction layers initially
19
+ """
20
+ super(CropDiseaseResNet50, self).__init__()
21
+
22
+ # Load pretrained ResNet50
23
+ if pretrained:
24
+ self.resnet = models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)
25
+ else:
26
+ self.resnet = models.resnet50(weights=None)
27
+
28
+ # Freeze feature extraction layers if specified
29
+ if freeze_features:
30
+ for param in self.resnet.parameters():
31
+ param.requires_grad = False
32
+
33
+ # Replace the final fully connected layer to match saved v2 model architecture
34
+ num_features = self.resnet.fc.in_features
35
+ self.resnet.fc = nn.Sequential(
36
+ nn.Dropout(0.5), # 0
37
+ nn.Linear(num_features, 1024), # 1
38
+ nn.BatchNorm1d(1024), # 2
39
+ nn.ReLU(inplace=True), # 3
40
+ nn.Dropout(0.3), # 4
41
+ nn.Linear(1024, 512), # 5
42
+ nn.BatchNorm1d(512), # 6
43
+ nn.ReLU(inplace=True), # 7
44
+ nn.Dropout(0.2), # 8
45
+ nn.Linear(512, num_classes) # 9
46
+ )
47
+
48
+ # Store number of classes
49
+ self.num_classes = num_classes
50
+
51
+ def forward(self, x):
52
+ """Forward pass"""
53
+ return self.resnet(x)
54
+
55
+ def unfreeze_features(self):
56
+ """Unfreeze all layers for fine-tuning"""
57
+ for param in self.resnet.parameters():
58
+ param.requires_grad = True
59
+
60
+ def freeze_features(self):
61
+ """Freeze feature extraction layers"""
62
+ for name, param in self.resnet.named_parameters():
63
+ if 'fc' not in name: # Don't freeze the classifier
64
+ param.requires_grad = False
65
+
66
+ def get_feature_extractor(self):
67
+ """Get feature extractor (without final FC layer) for Grad-CAM"""
68
+ return nn.Sequential(*list(self.resnet.children())[:-1])
69
+
70
+ def get_classifier(self):
71
+ """Get classifier layer for Grad-CAM"""
72
+ return self.resnet.fc
73
+
74
+ def create_model(num_classes, pretrained=True, device='cpu'):
75
+ """Create and initialize the model"""
76
+
77
+ model = CropDiseaseResNet50(
78
+ num_classes=num_classes,
79
+ pretrained=pretrained,
80
+ freeze_features=True
81
+ )
82
+
83
+ # Move to device
84
+ model = model.to(device)
85
+
86
+ return model
87
+
88
+ def get_model_summary(model, input_size=(3, 224, 224)):
89
+ """Print model summary"""
90
+
91
+ total_params = sum(p.numel() for p in model.parameters())
92
+ trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
93
+
94
+ print("=" * 60)
95
+ print("MODEL SUMMARY")
96
+ print("=" * 60)
97
+ print(f"Model: ResNet50 for Crop Disease Detection")
98
+ print(f"Input size: {input_size}")
99
+ print(f"Number of classes: {model.num_classes}")
100
+ print(f"Total parameters: {total_params:,}")
101
+ print(f"Trainable parameters: {trainable_params:,}")
102
+ print(f"Non-trainable parameters: {total_params - trainable_params:,}")
103
+ print("=" * 60)
104
+
105
+ return {
106
+ 'total_params': total_params,
107
+ 'trainable_params': trainable_params,
108
+ 'non_trainable_params': total_params - trainable_params
109
+ }
110
+
111
+ class ModelCheckpoint:
112
+ """Save best model checkpoints during training"""
113
+
114
+ def __init__(self, filepath, monitor='val_accuracy', mode='max', save_best_only=True):
115
+ self.filepath = filepath
116
+ self.monitor = monitor
117
+ self.mode = mode
118
+ self.save_best_only = save_best_only
119
+ self.best_score = float('-inf') if mode == 'max' else float('inf')
120
+
121
+ def __call__(self, model, optimizer, epoch, metrics):
122
+ """Save checkpoint if current score is better"""
123
+
124
+ current_score = metrics.get(self.monitor, 0)
125
+
126
+ is_better = False
127
+ if self.mode == 'max':
128
+ is_better = current_score > self.best_score
129
+ else:
130
+ is_better = current_score < self.best_score
131
+
132
+ if not self.save_best_only or is_better:
133
+ if is_better:
134
+ self.best_score = current_score
135
+
136
+ checkpoint = {
137
+ 'epoch': epoch,
138
+ 'model_state_dict': model.state_dict(),
139
+ 'optimizer_state_dict': optimizer.state_dict(),
140
+ 'metrics': metrics,
141
+ 'best_score': self.best_score
142
+ }
143
+
144
+ torch.save(checkpoint, self.filepath)
145
+
146
+ if is_better:
147
+ print(f"Saved new best model with {self.monitor}: {current_score:.4f}")
148
+
149
+ return True
150
+
151
+ return False
152
+
153
+ def load_checkpoint(filepath, model, optimizer=None, device='cpu'):
154
+ """Load model checkpoint"""
155
+
156
+ checkpoint = torch.load(filepath, map_location=device)
157
+
158
+ model.load_state_dict(checkpoint['model_state_dict'])
159
+
160
+ if optimizer is not None:
161
+ optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
162
+
163
+ epoch = checkpoint.get('epoch', 0)
164
+ metrics = checkpoint.get('metrics', {})
165
+ best_score = checkpoint.get('best_score', 0)
166
+
167
+ print(f"Loaded checkpoint from epoch {epoch}")
168
+ print(f"Best score: {best_score:.4f}")
169
+
170
+ return model, optimizer, epoch, metrics
171
+
172
+ if __name__ == "__main__":
173
+ # Test model creation
174
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
175
+ print(f"Using device: {device}")
176
+
177
+ # Create model for 17 classes (as per our dataset)
178
+ model = create_model(num_classes=17, device=device)
179
+
180
+ # Print model summary
181
+ get_model_summary(model)
182
+
183
+ # Test forward pass
184
+ dummy_input = torch.randn(1, 3, 224, 224).to(device)
185
+ output = model(dummy_input)
186
+ print(f"\nTest forward pass:")
187
+ print(f"Input shape: {dummy_input.shape}")
188
+ print(f"Output shape: {output.shape}")
189
+ print(f"Output probabilities sum: {torch.softmax(output, dim=1).sum():.4f}")
src/predict_cli.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Simple CLI for local image prediction (non-Docker)
3
+
4
+ Usage (PowerShell):
5
+ python -m src.predict_cli -i path\to\image.jpg -m models\crop_disease_v3_model.pth
6
+ """
7
+
8
+ import argparse
9
+ import json
10
+ from pathlib import Path
11
+
12
+ import torch
13
+ import torch.nn.functional as F
14
+ from PIL import Image
15
+ from torchvision import transforms
16
+
17
+ from .model import CropDiseaseResNet50
18
+
19
+ DEFAULT_CLASSES = [
20
+ 'Pepper__bell___Bacterial_spot',
21
+ 'Pepper__bell___healthy',
22
+ 'Potato___Early_blight',
23
+ 'Potato___healthy',
24
+ 'Potato___Late_blight',
25
+ 'Tomato__Target_Spot',
26
+ 'Tomato__Tomato_mosaic_virus',
27
+ 'Tomato__Tomato_YellowLeaf__Curl_Virus',
28
+ 'Tomato_Bacterial_spot',
29
+ 'Tomato_Early_blight',
30
+ 'Tomato_healthy',
31
+ 'Tomato_Late_blight',
32
+ 'Tomato_Leaf_Mold',
33
+ 'Tomato_Septoria_leaf_spot',
34
+ 'Tomato_Spider_mites_Two_spotted_spider_mite'
35
+ ]
36
+
37
+
38
+ def load_model(model_path: Path, class_names: list[str]) -> tuple[torch.nn.Module, torch.device]:
39
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
40
+ model = CropDiseaseResNet50(num_classes=len(class_names), pretrained=False)
41
+
42
+ if model_path.exists():
43
+ checkpoint = torch.load(str(model_path), map_location=device)
44
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
45
+ state_dict = checkpoint['model_state_dict']
46
+ if 'class_names' in checkpoint:
47
+ # Prefer class names bundled in checkpoint, if present
48
+ ckpt_classes = checkpoint['class_names']
49
+ if isinstance(ckpt_classes, list) and len(ckpt_classes) == len(class_names):
50
+ class_names = ckpt_classes
51
+ else:
52
+ state_dict = checkpoint
53
+ model.load_state_dict(state_dict, strict=True)
54
+ else:
55
+ print(f"Warning: model file not found at {model_path}, using untrained weights.")
56
+
57
+ model.to(device)
58
+ model.eval()
59
+ return model, device, class_names
60
+
61
+
62
+ def preprocess(image_path: Path) -> torch.Tensor:
63
+ image = Image.open(str(image_path)).convert('RGB')
64
+ transform = transforms.Compose([
65
+ transforms.Resize((224, 224)),
66
+ transforms.ToTensor(),
67
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
68
+ ])
69
+ return transform(image).unsqueeze(0)
70
+
71
+
72
+ def main():
73
+ parser = argparse.ArgumentParser(description='Local image prediction for crop disease detection')
74
+ parser.add_argument('-i', '--image', required=True, type=Path, help='Path to input image')
75
+ parser.add_argument('-m', '--model', default=Path('models/crop_disease_v3_model.pth'), type=Path, help='Path to model checkpoint (.pth)')
76
+ parser.add_argument('--classes', type=Path, help='Optional JSON file containing class names array')
77
+
78
+ args = parser.parse_args()
79
+
80
+ # Resolve class names
81
+ class_names = DEFAULT_CLASSES
82
+ if args.classes and args.classes.exists():
83
+ try:
84
+ class_names = json.loads(Path(args.classes).read_text(encoding='utf-8'))
85
+ except Exception:
86
+ print('Warning: Failed to read classes file, falling back to default classes.')
87
+
88
+ model, device, class_names = load_model(args.model, class_names)
89
+
90
+ input_tensor = preprocess(args.image).to(device)
91
+
92
+ with torch.no_grad():
93
+ outputs = model(input_tensor)
94
+ probabilities = F.softmax(outputs, dim=1)
95
+ confidence, predicted_idx = torch.max(probabilities, 1)
96
+
97
+ result = {
98
+ 'image': str(args.image),
99
+ 'predicted_class': class_names[predicted_idx.item()],
100
+ 'confidence': float(confidence.item())
101
+ }
102
+
103
+ print(json.dumps(result, indent=2))
104
+
105
+
106
+ if __name__ == '__main__':
107
+ main()
src/risk_level.py ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Risk Level Assessment for Crop Disease Detection
3
+ Calculates risk levels based on prediction confidence and disease severity
4
+ """
5
+
6
+ import json
7
+ from pathlib import Path
8
+ from datetime import datetime
9
+ from typing import Dict, List, Tuple, Optional
10
+
11
+ class RiskLevelCalculator:
12
+ """Calculate risk levels for crop disease predictions"""
13
+
14
+ def __init__(self, knowledge_base_path='knowledge_base/disease_info.json'):
15
+ """
16
+ Initialize risk calculator
17
+
18
+ Args:
19
+ knowledge_base_path: Path to disease knowledge base
20
+ """
21
+ self.knowledge_base_path = knowledge_base_path
22
+ self.disease_info = self._load_disease_info()
23
+
24
+ # Disease severity mapping (based on agricultural impact)
25
+ self.disease_severity = {
26
+ # Corn diseases
27
+ 'Corn___Cercospora_leaf_spot_Gray_leaf_spot': 'medium',
28
+ 'Corn___Common_rust': 'medium',
29
+ 'Corn___Northern_Leaf_Blight': 'high',
30
+ 'Corn___healthy': 'none',
31
+
32
+ # Potato diseases
33
+ 'Potato___Early_Blight': 'medium',
34
+ 'Potato___Late_Blight': 'high', # Most destructive
35
+ 'Potato___healthy': 'none',
36
+
37
+ # Tomato diseases
38
+ 'Tomato___Bacterial_spot': 'medium',
39
+ 'Tomato___Early_blight': 'medium',
40
+ 'Tomato___Late_blight': 'high', # Very destructive
41
+ 'Tomato___Leaf_Mold': 'low',
42
+ 'Tomato___Septoria_leaf_spot': 'medium',
43
+ 'Tomato___Spider_mites_Two_spotted_spider_mite': 'medium',
44
+ 'Tomato___Target_Spot': 'medium',
45
+ 'Tomato___Tomato_mosaic_virus': 'high', # Viral, no cure
46
+ 'Tomato___Tomato_Yellow_Leaf_Curl_Virus': 'high', # Viral, devastating
47
+ 'Tomato___healthy': 'none'
48
+ }
49
+
50
+ # Risk level thresholds
51
+ self.confidence_thresholds = {
52
+ 'high': 0.8,
53
+ 'medium': 0.5,
54
+ 'low': 0.0
55
+ }
56
+
57
+ def _load_disease_info(self):
58
+ """Load disease information from knowledge base"""
59
+ try:
60
+ with open(self.knowledge_base_path, 'r') as f:
61
+ data = json.load(f)
62
+ return {f"{d['crop']}___{d['disease']}": d for d in data['diseases']}
63
+ except FileNotFoundError:
64
+ print(f"Warning: Knowledge base not found at {self.knowledge_base_path}")
65
+ return {}
66
+
67
+ def calculate_base_risk(self, predicted_class: str, confidence: float) -> str:
68
+ """
69
+ Calculate base risk level using confidence and disease severity
70
+
71
+ Args:
72
+ predicted_class: Predicted disease class
73
+ confidence: Model confidence (0-1)
74
+
75
+ Returns:
76
+ risk_level: 'Low', 'Medium', or 'High'
77
+ """
78
+ # Handle healthy cases
79
+ if 'healthy' in predicted_class.lower():
80
+ return 'Low'
81
+
82
+ # Get disease severity
83
+ disease_severity = self.disease_severity.get(predicted_class, 'medium')
84
+
85
+ # Calculate risk based on confidence and severity
86
+ if confidence >= self.confidence_thresholds['high']:
87
+ # High confidence predictions
88
+ if disease_severity == 'high':
89
+ return 'High'
90
+ elif disease_severity == 'medium':
91
+ return 'Medium'
92
+ else:
93
+ return 'Low'
94
+
95
+ elif confidence >= self.confidence_thresholds['medium']:
96
+ # Medium confidence predictions
97
+ if disease_severity == 'high':
98
+ return 'High'
99
+ elif disease_severity == 'medium':
100
+ return 'Medium'
101
+ else:
102
+ return 'Low'
103
+
104
+ else:
105
+ # Low confidence predictions
106
+ if disease_severity == 'high':
107
+ return 'Medium'
108
+ else:
109
+ return 'Low'
110
+
111
+ def calculate_enhanced_risk(self, predicted_class: str, confidence: float,
112
+ weather_data: Optional[Dict] = None,
113
+ growth_stage: Optional[str] = None) -> Dict:
114
+ """
115
+ Calculate enhanced risk level with environmental factors
116
+
117
+ Args:
118
+ predicted_class: Predicted disease class
119
+ confidence: Model confidence (0-1)
120
+ weather_data: Optional weather information
121
+ growth_stage: Optional crop growth stage
122
+
123
+ Returns:
124
+ risk_assessment: Detailed risk assessment
125
+ """
126
+ # Base risk calculation
127
+ base_risk = self.calculate_base_risk(predicted_class, confidence)
128
+
129
+ # Initialize risk factors
130
+ risk_factors = []
131
+ risk_multiplier = 1.0
132
+
133
+ # Weather-based risk adjustment
134
+ if weather_data:
135
+ weather_risk, weather_factors = self._assess_weather_risk(
136
+ predicted_class, weather_data
137
+ )
138
+ risk_factors.extend(weather_factors)
139
+ risk_multiplier *= weather_risk
140
+
141
+ # Growth stage risk adjustment
142
+ if growth_stage:
143
+ stage_risk, stage_factors = self._assess_growth_stage_risk(
144
+ predicted_class, growth_stage
145
+ )
146
+ risk_factors.extend(stage_factors)
147
+ risk_multiplier *= stage_risk
148
+
149
+ # Calculate final risk level
150
+ final_risk = self._adjust_risk_level(base_risk, risk_multiplier)
151
+
152
+ return {
153
+ 'risk_level': final_risk,
154
+ 'base_risk': base_risk,
155
+ 'confidence': confidence,
156
+ 'disease_severity': self.disease_severity.get(predicted_class, 'unknown'),
157
+ 'risk_factors': risk_factors,
158
+ 'risk_multiplier': risk_multiplier,
159
+ 'assessment_timestamp': datetime.now().isoformat(),
160
+ 'recommendations': self._get_risk_recommendations(final_risk, predicted_class)
161
+ }
162
+
163
+ def _assess_weather_risk(self, predicted_class: str, weather_data: Dict) -> Tuple[float, List[str]]:
164
+ """Assess weather-based risk factors"""
165
+ risk_multiplier = 1.0
166
+ factors = []
167
+
168
+ humidity = weather_data.get('humidity', 50)
169
+ temperature = weather_data.get('temperature', 25)
170
+ rainfall = weather_data.get('rainfall', 0)
171
+
172
+ # Disease-specific weather risk
173
+ if 'Late_Blight' in predicted_class or 'Late_blight' in predicted_class:
174
+ # Late blight thrives in cool, humid conditions
175
+ if humidity > 80 and temperature < 20:
176
+ risk_multiplier *= 1.5
177
+ factors.append("High humidity and cool temperature favor late blight")
178
+ if rainfall > 10:
179
+ risk_multiplier *= 1.3
180
+ factors.append("Recent rainfall increases late blight risk")
181
+
182
+ elif 'rust' in predicted_class.lower():
183
+ # Rust diseases favor cool, humid conditions
184
+ if humidity > 70 and 15 < temperature < 25:
185
+ risk_multiplier *= 1.4
186
+ factors.append("Cool, humid conditions favor rust development")
187
+
188
+ elif 'Early_Blight' in predicted_class or 'Early_blight' in predicted_class:
189
+ # Early blight thrives in warm, humid conditions
190
+ if humidity > 75 and temperature > 25:
191
+ risk_multiplier *= 1.4
192
+ factors.append("Warm, humid conditions favor early blight")
193
+
194
+ elif 'Spider_mites' in predicted_class:
195
+ # Spider mites thrive in hot, dry conditions
196
+ if humidity < 40 and temperature > 30:
197
+ risk_multiplier *= 1.6
198
+ factors.append("Hot, dry conditions favor spider mite infestations")
199
+
200
+ return risk_multiplier, factors
201
+
202
+ def _assess_growth_stage_risk(self, predicted_class: str, growth_stage: str) -> Tuple[float, List[str]]:
203
+ """Assess growth stage-based risk factors"""
204
+ risk_multiplier = 1.0
205
+ factors = []
206
+
207
+ # Critical growth stages for different diseases
208
+ if growth_stage.lower() in ['flowering', 'fruit_development']:
209
+ if 'Late_Blight' in predicted_class or 'Late_blight' in predicted_class:
210
+ risk_multiplier *= 1.3
211
+ factors.append("Late blight is particularly damaging during flowering/fruiting")
212
+
213
+ elif 'virus' in predicted_class.lower():
214
+ risk_multiplier *= 1.4
215
+ factors.append("Viral infections during flowering severely impact yield")
216
+
217
+ elif growth_stage.lower() in ['seedling', 'early_vegetative']:
218
+ risk_multiplier *= 1.2
219
+ factors.append("Young plants are more vulnerable to disease damage")
220
+
221
+ return risk_multiplier, factors
222
+
223
+ def _adjust_risk_level(self, base_risk: str, multiplier: float) -> str:
224
+ """Adjust risk level based on multiplier"""
225
+ risk_levels = ['Low', 'Medium', 'High']
226
+ current_index = risk_levels.index(base_risk)
227
+
228
+ if multiplier >= 1.5:
229
+ # Increase risk level
230
+ new_index = min(current_index + 1, len(risk_levels) - 1)
231
+ elif multiplier <= 0.7:
232
+ # Decrease risk level
233
+ new_index = max(current_index - 1, 0)
234
+ else:
235
+ new_index = current_index
236
+
237
+ return risk_levels[new_index]
238
+
239
+ def _get_risk_recommendations(self, risk_level: str, predicted_class: str) -> List[str]:
240
+ """Get recommendations based on risk level"""
241
+ recommendations = []
242
+
243
+ if risk_level == 'High':
244
+ recommendations.extend([
245
+ "🚨 IMMEDIATE ACTION REQUIRED",
246
+ "Apply appropriate treatment immediately",
247
+ "Monitor field daily for disease spread",
248
+ "Consider emergency harvest if disease is severe",
249
+ "Consult agricultural extension services"
250
+ ])
251
+
252
+ elif risk_level == 'Medium':
253
+ recommendations.extend([
254
+ "⚠️ MONITOR CLOSELY",
255
+ "Apply preventive treatments",
256
+ "Increase monitoring frequency",
257
+ "Prepare for potential treatment application",
258
+ "Check weather forecasts for favorable disease conditions"
259
+ ])
260
+
261
+ else: # Low risk
262
+ recommendations.extend([
263
+ "✅ CONTINUE MONITORING",
264
+ "Maintain regular field inspections",
265
+ "Follow standard preventive practices",
266
+ "Keep treatment options ready"
267
+ ])
268
+
269
+ # Add disease-specific recommendations
270
+ if 'healthy' not in predicted_class.lower():
271
+ disease_info = self.disease_info.get(predicted_class, {})
272
+ if 'solutions' in disease_info:
273
+ recommendations.extend(disease_info['solutions'][:3]) # Top 3 solutions
274
+
275
+ return recommendations
276
+
277
+ def get_risk_summary(self, predictions: List[Dict]) -> Dict:
278
+ """
279
+ Generate risk summary for multiple predictions
280
+
281
+ Args:
282
+ predictions: List of prediction dictionaries
283
+
284
+ Returns:
285
+ summary: Risk summary across all predictions
286
+ """
287
+ if not predictions:
288
+ return {'overall_risk': 'Low', 'total_predictions': 0}
289
+
290
+ risk_counts = {'High': 0, 'Medium': 0, 'Low': 0}
291
+ total_confidence = 0
292
+ diseases_detected = []
293
+
294
+ for pred in predictions:
295
+ risk_level = pred.get('risk_level', 'Low')
296
+ risk_counts[risk_level] += 1
297
+ total_confidence += pred.get('confidence', 0)
298
+
299
+ if 'healthy' not in pred.get('predicted_class', '').lower():
300
+ diseases_detected.append(pred.get('predicted_class', ''))
301
+
302
+ # Determine overall risk
303
+ if risk_counts['High'] > 0:
304
+ overall_risk = 'High'
305
+ elif risk_counts['Medium'] > 0:
306
+ overall_risk = 'Medium'
307
+ else:
308
+ overall_risk = 'Low'
309
+
310
+ return {
311
+ 'overall_risk': overall_risk,
312
+ 'risk_distribution': risk_counts,
313
+ 'total_predictions': len(predictions),
314
+ 'average_confidence': total_confidence / len(predictions),
315
+ 'diseases_detected': len(set(diseases_detected)),
316
+ 'unique_diseases': list(set(diseases_detected)),
317
+ 'assessment_timestamp': datetime.now().isoformat()
318
+ }
319
+
320
+ def test_risk_calculator():
321
+ """Test risk level calculator"""
322
+ print("🎯 Testing Risk Level Calculator...")
323
+
324
+ calculator = RiskLevelCalculator()
325
+
326
+ # Test cases
327
+ test_cases = [
328
+ ('Potato___Late_Blight', 0.95),
329
+ ('Tomato___healthy', 0.88),
330
+ ('Corn___Northern_Leaf_Blight', 0.65),
331
+ ('Tomato___Spider_mites_Two_spotted_spider_mite', 0.45)
332
+ ]
333
+
334
+ print("\n📊 Risk Assessment Results:")
335
+ print("-" * 60)
336
+
337
+ for disease, confidence in test_cases:
338
+ # Basic risk assessment
339
+ basic_risk = calculator.calculate_base_risk(disease, confidence)
340
+
341
+ # Enhanced risk assessment with weather
342
+ weather_data = {
343
+ 'humidity': 85,
344
+ 'temperature': 18,
345
+ 'rainfall': 15
346
+ }
347
+
348
+ enhanced_risk = calculator.calculate_enhanced_risk(
349
+ disease, confidence, weather_data, 'flowering'
350
+ )
351
+
352
+ print(f"Disease: {disease}")
353
+ print(f"Confidence: {confidence:.1%}")
354
+ print(f"Basic Risk: {basic_risk}")
355
+ print(f"Enhanced Risk: {enhanced_risk['risk_level']}")
356
+ print(f"Risk Factors: {len(enhanced_risk['risk_factors'])}")
357
+ print("-" * 60)
358
+
359
+ print("✅ Risk Level Calculator tested successfully!")
360
+ return True
361
+
362
+ if __name__ == "__main__":
363
+ test_risk_calculator()
src/train.py ADDED
@@ -0,0 +1,325 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Training script for crop disease detection model
3
+ """
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.optim as optim
8
+ from torch.optim.lr_scheduler import ReduceLROnPlateau, StepLR
9
+ import time
10
+ import copy
11
+ import json
12
+ from pathlib import Path
13
+ import matplotlib.pyplot as plt
14
+ from sklearn.metrics import classification_report, confusion_matrix
15
+ import numpy as np
16
+
17
+ from dataset import create_data_loaders, get_class_weights
18
+ from model import create_model, ModelCheckpoint, get_model_summary
19
+
20
+ class Trainer:
21
+ """Training class for crop disease detection model"""
22
+
23
+ def __init__(self, model, train_loader, val_loader, class_names, device='cpu'):
24
+ self.model = model
25
+ self.train_loader = train_loader
26
+ self.val_loader = val_loader
27
+ self.class_names = class_names
28
+ self.device = device
29
+
30
+ # Training history
31
+ self.history = {
32
+ 'train_loss': [],
33
+ 'train_acc': [],
34
+ 'val_loss': [],
35
+ 'val_acc': [],
36
+ 'lr': []
37
+ }
38
+
39
+ def train_epoch(self, criterion, optimizer):
40
+ """Train for one epoch"""
41
+ self.model.train()
42
+ running_loss = 0.0
43
+ running_corrects = 0
44
+ total_samples = 0
45
+
46
+ for inputs, labels in self.train_loader:
47
+ inputs = inputs.to(self.device)
48
+ labels = labels.to(self.device)
49
+
50
+ # Zero gradients
51
+ optimizer.zero_grad()
52
+
53
+ # Forward pass
54
+ outputs = self.model(inputs)
55
+ _, preds = torch.max(outputs, 1)
56
+ loss = criterion(outputs, labels)
57
+
58
+ # Backward pass
59
+ loss.backward()
60
+ optimizer.step()
61
+
62
+ # Statistics
63
+ running_loss += loss.item() * inputs.size(0)
64
+ running_corrects += torch.sum(preds == labels.data)
65
+ total_samples += inputs.size(0)
66
+
67
+ epoch_loss = running_loss / total_samples
68
+ epoch_acc = running_corrects.double() / total_samples
69
+
70
+ return epoch_loss, epoch_acc.item()
71
+
72
+ def validate_epoch(self, criterion):
73
+ """Validate for one epoch"""
74
+ self.model.eval()
75
+ running_loss = 0.0
76
+ running_corrects = 0
77
+ total_samples = 0
78
+
79
+ with torch.no_grad():
80
+ for inputs, labels in self.val_loader:
81
+ inputs = inputs.to(self.device)
82
+ labels = labels.to(self.device)
83
+
84
+ # Forward pass
85
+ outputs = self.model(inputs)
86
+ _, preds = torch.max(outputs, 1)
87
+ loss = criterion(outputs, labels)
88
+
89
+ # Statistics
90
+ running_loss += loss.item() * inputs.size(0)
91
+ running_corrects += torch.sum(preds == labels.data)
92
+ total_samples += inputs.size(0)
93
+
94
+ epoch_loss = running_loss / total_samples
95
+ epoch_acc = running_corrects.double() / total_samples
96
+
97
+ return epoch_loss, epoch_acc.item()
98
+
99
+ def train(self, num_epochs=25, learning_rate=1e-4, weight_decay=1e-4,
100
+ use_class_weights=True, checkpoint_path='models/crop_disease_resnet50.pth',
101
+ fine_tune_epoch=10):
102
+ """
103
+ Train the model
104
+
105
+ Args:
106
+ num_epochs: Number of training epochs
107
+ learning_rate: Initial learning rate
108
+ weight_decay: Weight decay for regularization
109
+ use_class_weights: Use class weights for imbalanced data
110
+ checkpoint_path: Path to save best model
111
+ fine_tune_epoch: Epoch to start fine-tuning (unfreeze all layers)
112
+ """
113
+
114
+ print("Starting training...")
115
+ print(f"Device: {self.device}")
116
+ print(f"Number of classes: {len(self.class_names)}")
117
+ print(f"Training samples: {len(self.train_loader.dataset)}")
118
+ print(f"Validation samples: {len(self.val_loader.dataset)}")
119
+
120
+ # Setup loss function
121
+ if use_class_weights:
122
+ class_weights = get_class_weights('data')
123
+ class_weights = class_weights.to(self.device)
124
+ criterion = nn.CrossEntropyLoss(weight=class_weights)
125
+ print("Using weighted CrossEntropyLoss")
126
+ else:
127
+ criterion = nn.CrossEntropyLoss()
128
+ print("Using standard CrossEntropyLoss")
129
+
130
+ # Setup optimizer
131
+ optimizer = optim.Adam(
132
+ filter(lambda p: p.requires_grad, self.model.parameters()),
133
+ lr=learning_rate,
134
+ weight_decay=weight_decay
135
+ )
136
+
137
+ # Setup learning rate scheduler
138
+ scheduler = ReduceLROnPlateau(
139
+ optimizer, mode='max', factor=0.5, patience=5
140
+ )
141
+
142
+ # Setup model checkpoint
143
+ checkpoint = ModelCheckpoint(
144
+ filepath=checkpoint_path,
145
+ monitor='val_accuracy',
146
+ mode='max'
147
+ )
148
+
149
+ # Training loop
150
+ best_acc = 0.0
151
+ start_time = time.time()
152
+
153
+ for epoch in range(num_epochs):
154
+ epoch_start = time.time()
155
+
156
+ # Fine-tuning: unfreeze all layers after specified epoch
157
+ if epoch == fine_tune_epoch:
158
+ print(f"\nEpoch {epoch}: Starting fine-tuning (unfreezing all layers)")
159
+ self.model.unfreeze_features()
160
+ # Reduce learning rate for fine-tuning
161
+ for param_group in optimizer.param_groups:
162
+ param_group['lr'] = learning_rate * 0.1
163
+ print(f"Reduced learning rate to: {optimizer.param_groups[0]['lr']}")
164
+
165
+ # Training phase
166
+ train_loss, train_acc = self.train_epoch(criterion, optimizer)
167
+
168
+ # Validation phase
169
+ val_loss, val_acc = self.validate_epoch(criterion)
170
+
171
+ # Update learning rate
172
+ scheduler.step(val_acc)
173
+ current_lr = optimizer.param_groups[0]['lr']
174
+
175
+ # Save history
176
+ self.history['train_loss'].append(train_loss)
177
+ self.history['train_acc'].append(train_acc)
178
+ self.history['val_loss'].append(val_loss)
179
+ self.history['val_acc'].append(val_acc)
180
+ self.history['lr'].append(current_lr)
181
+
182
+ # Save checkpoint
183
+ metrics = {
184
+ 'val_accuracy': val_acc,
185
+ 'val_loss': val_loss,
186
+ 'train_accuracy': train_acc,
187
+ 'train_loss': train_loss
188
+ }
189
+ checkpoint(self.model, optimizer, epoch, metrics)
190
+
191
+ # Update best accuracy
192
+ if val_acc > best_acc:
193
+ best_acc = val_acc
194
+
195
+ # Print progress
196
+ epoch_time = time.time() - epoch_start
197
+ print(f'Epoch {epoch+1:2d}/{num_epochs} | '
198
+ f'Train Loss: {train_loss:.4f} Acc: {train_acc:.4f} | '
199
+ f'Val Loss: {val_loss:.4f} Acc: {val_acc:.4f} | '
200
+ f'LR: {current_lr:.2e} | Time: {epoch_time:.1f}s')
201
+
202
+ # Training completed
203
+ total_time = time.time() - start_time
204
+ print(f'\nTraining completed in {total_time//60:.0f}m {total_time%60:.0f}s')
205
+ print(f'Best validation accuracy: {best_acc:.4f}')
206
+
207
+ # Save training history
208
+ self.save_training_history()
209
+
210
+ return self.model, self.history
211
+
212
+ def save_training_history(self, filepath='outputs/training_history.json'):
213
+ """Save training history to file"""
214
+ Path(filepath).parent.mkdir(parents=True, exist_ok=True)
215
+
216
+ with open(filepath, 'w') as f:
217
+ json.dump(self.history, f, indent=2)
218
+
219
+ print(f"Training history saved to: {filepath}")
220
+
221
+ def plot_training_curves(self, save_path='outputs/training_curves.png'):
222
+ """Plot and save training curves"""
223
+ fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 10))
224
+
225
+ epochs = range(1, len(self.history['train_loss']) + 1)
226
+
227
+ # Loss curves
228
+ ax1.plot(epochs, self.history['train_loss'], 'b-', label='Training Loss')
229
+ ax1.plot(epochs, self.history['val_loss'], 'r-', label='Validation Loss')
230
+ ax1.set_title('Training and Validation Loss')
231
+ ax1.set_xlabel('Epoch')
232
+ ax1.set_ylabel('Loss')
233
+ ax1.legend()
234
+ ax1.grid(True)
235
+
236
+ # Accuracy curves
237
+ ax2.plot(epochs, self.history['train_acc'], 'b-', label='Training Accuracy')
238
+ ax2.plot(epochs, self.history['val_acc'], 'r-', label='Validation Accuracy')
239
+ ax2.set_title('Training and Validation Accuracy')
240
+ ax2.set_xlabel('Epoch')
241
+ ax2.set_ylabel('Accuracy')
242
+ ax2.legend()
243
+ ax2.grid(True)
244
+
245
+ # Learning rate
246
+ ax3.plot(epochs, self.history['lr'], 'g-', label='Learning Rate')
247
+ ax3.set_title('Learning Rate Schedule')
248
+ ax3.set_xlabel('Epoch')
249
+ ax3.set_ylabel('Learning Rate')
250
+ ax3.set_yscale('log')
251
+ ax3.legend()
252
+ ax3.grid(True)
253
+
254
+ # Combined accuracy
255
+ ax4.plot(epochs, self.history['train_acc'], 'b-', label='Training')
256
+ ax4.plot(epochs, self.history['val_acc'], 'r-', label='Validation')
257
+ ax4.set_title('Model Accuracy Comparison')
258
+ ax4.set_xlabel('Epoch')
259
+ ax4.set_ylabel('Accuracy')
260
+ ax4.legend()
261
+ ax4.grid(True)
262
+
263
+ plt.tight_layout()
264
+ plt.savefig(save_path, dpi=300, bbox_inches='tight')
265
+ plt.close()
266
+
267
+ print(f"Training curves saved to: {save_path}")
268
+
269
+ def main():
270
+ """Main training function"""
271
+
272
+ # Configuration
273
+ config = {
274
+ 'data_dir': 'data',
275
+ 'batch_size': 32, # Increased for GPU training
276
+ 'num_epochs': 20,
277
+ 'learning_rate': 1e-4,
278
+ 'weight_decay': 1e-4,
279
+ 'fine_tune_epoch': 10,
280
+ 'checkpoint_path': 'models/crop_disease_resnet50.pth'
281
+ }
282
+
283
+ # Device setup
284
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
285
+ print(f"Using device: {device}")
286
+ if torch.cuda.is_available():
287
+ print(f"GPU: {torch.cuda.get_device_name(0)}")
288
+ print(f"GPU Memory: {torch.cuda.get_device_properties(0).total_memory / 1024**3:.1f} GB")
289
+
290
+ # Create data loaders
291
+ print("Loading dataset...")
292
+ train_loader, val_loader, test_loader, class_names = create_data_loaders(
293
+ data_dir=config['data_dir'],
294
+ batch_size=config['batch_size'],
295
+ num_workers=0 if device.type == 'cpu' else 2 # Use more workers for GPU
296
+ )
297
+
298
+ print(f"Dataset loaded: {len(class_names)} classes")
299
+ print(f"Classes: {class_names}")
300
+
301
+ # Create model
302
+ print("Creating model...")
303
+ model = create_model(num_classes=len(class_names), device=device)
304
+ get_model_summary(model)
305
+
306
+ # Create trainer
307
+ trainer = Trainer(model, train_loader, val_loader, class_names, device)
308
+
309
+ # Start training
310
+ trained_model, history = trainer.train(
311
+ num_epochs=config['num_epochs'],
312
+ learning_rate=config['learning_rate'],
313
+ weight_decay=config['weight_decay'],
314
+ checkpoint_path=config['checkpoint_path'],
315
+ fine_tune_epoch=config['fine_tune_epoch']
316
+ )
317
+
318
+ # Plot training curves
319
+ trainer.plot_training_curves()
320
+
321
+ print("\nTraining completed successfully!")
322
+ print(f"Best model saved at: {config['checkpoint_path']}")
323
+
324
+ if __name__ == "__main__":
325
+ main()
test_leaf_sample.jpg ADDED