| # Lung Cancer Classification API with Grad-CAM |
|
|
| A production-ready Flask REST API for classifying lung cancer types using DenseNet121 with Grad-CAM visualization. Validates medical images, provides explainable AI predictions, and returns base64-encoded visualizations. |
|
|
| ## Features |
|
|
| - β
**DenseNet121 Classification**: 4-class lung cancer type prediction (Adenocarcinoma, Small Cell, Large Cell, Squamous Cell) |
| - β
**Grad-CAM Visualization**: Visual explanation of model predictions via heatmap overlays |
| - β
**CT Scan Validation**: Automatic rejection of non-medical (color) images |
| - β
**Dual Preprocessing**: Attempts both normalized and non-normalized preprocessing for robust predictions |
| - β
**REST API**: Flask with Swagger UI documentation |
| - β
**CORS Support**: Mobile and cross-origin requests enabled |
| - β
**Production Ready**: Gunicorn WSGI server, environment variables, Docker-compatible |
| - β
**JSON Responses**: Base64-encoded images for mobile integration |
|
|
| ## Installation |
|
|
| ### Local Development |
|
|
| ```bash |
| # Clone the repository |
| git clone <repo-url> |
| cd Grad-Cam-Backend |
| |
| # Create virtual environment |
| python3.11 -m venv .venv |
| source .venv/bin/activate # On Windows: .venv\Scripts\activate |
| |
| # Install dependencies |
| pip install -r requirements.txt |
| ``` |
|
|
| ### Docker (Optional) |
|
|
| ```bash |
| docker build -t lung-cancer-api . |
| docker run -p 5001:5001 lung-cancer-api |
| ``` |
|
|
| ## Usage |
|
|
| ### Running Locally |
|
|
| ```bash |
| # Development (debug mode enabled) |
| DEBUG=true python app.py |
| |
| # Production (debug mode disabled) |
| python app.py |
| |
| # Or use Gunicorn |
| gunicorn app:app --bind 0.0.0.0:5001 |
| ``` |
|
|
| ### Running Tests |
|
|
| Classify a single image using the command-line tool: |
|
|
| ```bash |
| source .venv/bin/activate |
| python test.py /path/to/ct_scan.jpg |
| ``` |
|
|
| Example output: |
| ``` |
| ============================================================ |
| π©Ί LUNG CANCER CLASSIFICATION - DenseNet121 |
| ============================================================ |
| π Image: ct_scan.jpg |
| π Size: (512, 512, 3) |
| |
| π Running classification... |
| |
| ============================================================ |
| π CLASSIFICATION RESULTS |
| ============================================================ |
| Adenocarcinoma (Class A) ββββββββββββββββββββ 45.23% |
| Small Cell (Class B) ββββββββββββββββββββ 72.15% |
| Large Cell (Class E) ββββββββββββββββββββ 12.50% |
| Squamous Cell (Class G) ββββββββββββββββββββ 8.12% |
| |
| ============================================================ |
| π₯ FINAL DIAGNOSIS |
| ============================================================ |
| Classification: Small Cell (Class B) |
| Confidence: 72.15% |
| ============================================================ |
| |
| β
Result saved to 'classification_result.png' |
| ``` |
| |
| ### REST API |
| |
| **Base URL**: `http://localhost:5001` |
| |
| #### 1. Health Check |
| ```bash |
| GET /health |
| ``` |
| |
| Response: `{"status": "healthy", "model_loaded": true}` |
| |
| #### 2. CT Scan Validation (No Classification) |
| ```bash |
| POST /validate-ct |
| Content-Type: multipart/form-data |
|
|
| file: <image_file> |
| ``` |
| |
| Response: |
| ```json |
| { |
| "is_ct_scan": true, |
| "color_score": 4.5, |
| "message": "Valid CT scan - grayscale image detected" |
| } |
| ``` |
| |
| #### 3. Full Analysis with Grad-CAM |
| ```bash |
| POST /analyze |
| Content-Type: multipart/form-data |
| |
| file: <image_file> |
| ``` |
| |
| Response (on success): |
| ```json |
| { |
| "success": true, |
| "prediction": "Small Cell (Class B)", |
| "confidence": 72.15, |
| "all_confidences": { |
| "Adenocarcinoma (Class A)": 45.23, |
| "Small Cell (Class B)": 72.15, |
| "Large Cell (Class E)": 12.50, |
| "Squamous Cell (Class G)": 8.12 |
| }, |
| "original_image": "base64_encoded_jpeg_string", |
| "heatmap_image": "base64_encoded_gradcam_heatmap" |
| } |
| ``` |
| |
| ### Swagger UI |
|
|
| Interactive API documentation available at: **http://localhost:5001/docs** |
|
|
| ## Deployment |
|
|
| ### Railway.app Deployment |
|
|
| 1. **Create a Railway account** at https://railway.app |
|
|
| 2. **Connect your GitHub repository** |
| - Go to Railway dashboard |
| - Click "New Project" β "Deploy from GitHub repo" |
| - Select this repository |
|
|
| 3. **Configure environment variables** in Railway dashboard: |
| ``` |
| DEBUG=False |
| PORT=5001 |
| ``` |
|
|
| 4. **Deploy** |
| - Railway automatically detects `Procfile` and deploys the app |
| - Your API will be available at `https://<your-project>.up.railway.app` |
|
|
| ### Heroku Deployment (Legacy) |
|
|
| ```bash |
| heroku create <app-name> |
| heroku config:set DEBUG=False |
| git push heroku main |
| ``` |
|
|
| ## API Validation |
|
|
| ### CT Scan Validation Threshold |
|
|
| - **Valid CT scans**: Color score < 6.0 (grayscale images) |
| - **Rejected**: Color score β₯ 6.0 (color photos, non-medical images) |
|
|
| Color score measures RGB channel variance: |
| - Pure grayscale: 0-2 |
| - Real CT scans: 2-6 |
| - Color photos: 7-100 |
|
|
| ### Confidence Threshold |
|
|
| - **Accepted**: Confidence β₯ 50% |
| - **Rejected**: Confidence < 50% (uncertain predictions) |
|
|
| ## Configuration |
|
|
| ### Environment Variables |
|
|
| Create a `.env` file (or set via environment): |
|
|
| ```bash |
| # Flask settings |
| DEBUG=False # Set to True for development |
| PORT=5001 # Server port (defaults to 5001) |
| |
| # Optional overrides |
| # MODEL_PATH=models/densenet_final_classification.pth |
| ``` |
|
|
| ## Model Architecture |
|
|
| ``` |
| DenseNet121 (Feature Extractor) |
| β |
| [2176 channels] |
| β |
| Classifier: |
| - ReLU Activation |
| - Linear(2176 β 4) [outputs class logits] |
| β |
| Softmax Probabilities |
| β |
| 4-Class Output: |
| [Adenocarcinoma, Small Cell, Large Cell, Squamous Cell] |
| ``` |
|
|
| ## Preprocessing Pipeline |
|
|
| The API uses **dual preprocessing** for robustness: |
|
|
| 1. **Normalized**: ImageNet normalization (mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) |
| 2. **Non-normalized**: Raw tensor scaling |
|
|
| Algorithm selects whichever produces higher max confidence, adapting to different training conditions. |
|
|
| ## Grad-CAM Explanation |
|
|
| Grad-CAM (Gradient-weighted Class Activation Mapping) highlights regions the model focuses on: |
|
|
| - **Red/Hot regions**: Model focuses heavily (high confidence) |
| - **Blue/Cool regions**: Model has low attention (less relevant) |
| - **Alpha blending**: 0.4 transparency for visualization |
|
|
| Extracted from: `model.features.norm5` (last fully-connected layer before classifier) |
|
|
| ## Error Handling |
|
|
| | Status | Error | Solution | |
| |--------|-------|----------| |
| | 400 | "No file uploaded" | Ensure multipart/form-data with 'file' field | |
| | 400 | "Invalid input: image does not appear to be a CT scan" | Use actual CT scan (grayscale medical image) | |
| | 400 | "Confidence too low" | Model uncertain; try another image | |
| | 500 | "Error processing image" | Check server logs; verify model file exists | |
|
|
| ## File Structure |
|
|
| ``` |
| Grad-Cam-Backend/ |
| βββ app.py # Main Flask application |
| βββ test.py # Command-line classification tool |
| βββ models/ |
| β βββ densenet_final_classification.pth # DenseNet weights |
| βββ requirements.txt # Python dependencies |
| βββ Procfile # Railway deployment config |
| βββ runtime.txt # Python version (3.11.14) |
| βββ .env.example # Example environment variables |
| βββ .gitignore # Git ignore patterns |
| βββ README.md # This file |
| ``` |
|
|
| ## Class Labels |
|
|
| | Index | Class | Description | |
| |-------|-------|-------------| |
| | 0 | Adenocarcinoma (Class A) | Most common; develops in glandular cells | |
| | 1 | Small Cell (Class B) | Aggressive; fast-growing variant | |
| | 2 | Large Cell (Class E) | Rare; large undifferentiated cells | |
| | 3 | Squamous Cell (Class G) | Develops in flat cells lining airways | |
|
|
| ## Performance Tuning |
|
|
| ### Increase Sensitivity |
| ```python |
| CONFIDENCE_THRESHOLD = 0.3 # Lower from 0.5 to accept more predictions |
| # In app.py line ~180 |
| ``` |
|
|
| ### Adjust CT Validation |
| ```python |
| is_valid = color_score < 8.0 # Raise from 6.0 if rejecting true CTs with color compression |
| # In app.py line ~157 |
| ``` |
|
|
| ## Troubleshooting |
|
|
| ### Model fails to load |
| ``` |
| RuntimeError: Error(s) in loading state_dict for DenseNet: |
| Missing key(s) in state_dict: ... |
| ``` |
| **Solution**: Ensure `models/densenet_final_classification.pth` exists and matches architecture in `load_model()`. |
|
|
| ### Port already in use |
| ```bash |
| # Find process using port 5001 |
| lsof -i :5001 |
| |
| # Kill the process |
| kill -9 <PID> |
| |
| # Or use different port |
| PORT=5002 python app.py |
| ``` |
|
|
| ### Module import errors |
| ```bash |
| # Ensure all dependencies installed |
| pip install -r requirements.txt |
| |
| # Verify virtual environment active |
| source .venv/bin/activate |
| ``` |
|
|
| ## API Examples |
|
|
| ### Python Client |
|
|
| ```python |
| import requests |
| import base64 |
| from PIL import Image |
| from io import BytesIO |
| |
| API_URL = "http://localhost:5001" |
| |
| # Upload and classify CT scan |
| with open("ct_scan.jpg", "rb") as f: |
| files = {"file": f} |
| response = requests.post(f"{API_URL}/analyze", files=files) |
| |
| result = response.json() |
| print(f"Diagnosis: {result['prediction']}") |
| print(f"Confidence: {result['confidence']}%") |
| |
| # Decode and view heatmap |
| heatmap_data = base64.b64decode(result['heatmap_image']) |
| heatmap_img = Image.open(BytesIO(heatmap_data)) |
| heatmap_img.show() |
| ``` |
|
|
| ### JavaScript/Flutter Client |
|
|
| ```javascript |
| const formData = new FormData(); |
| formData.append('file', imageFile); |
| |
| const response = await fetch('http://localhost:5001/analyze', { |
| method: 'POST', |
| body: formData |
| }); |
| |
| const result = await response.json(); |
| console.log(`Diagnosis: ${result.prediction}`); |
| console.log(`Confidence: ${result.confidence}%`); |
| |
| // Display heatmap from base64 |
| const img = new Image(); |
| img.src = `data:image/jpeg;base64,${result.heatmap_image}`; |
| ``` |
|
|
| ## Dependencies |
|
|
| - **Flask 3.0.0**: REST framework |
| - **Gunicorn 21.2.0**: WSGI server |
| - **PyTorch 2.1.0**: Deep learning |
| - **TorchVision 0.16.0**: Computer vision |
| - **OpenCV 4.8.1.78**: Image processing |
| - **Pillow 10.1.0**: Image I/O |
| - **NumPy 1.24.3**: Array operations |
| - **Flask-CORS 4.0.0**: Cross-origin support |
| - **Flasgger 0.9.7.1**: Swagger API docs |
|
|
| ## License |
|
|
| Proprietary - Medical Research Use Only |
|
|
| ## Support |
|
|
| For issues, email: support@lungcancerapi.com |
|
|
| --- |
|
|
| **Last Updated**: March 8, 2026 |
| **Version**: 1.0.0 |
| **Status**: Production Ready β
|
|
|