itsluckysharma01's picture
Upload 57 files
b5f303c verified
import os
import numpy as np
import tensorflow as tf
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, send_from_directory, send_file
from werkzeug.utils import secure_filename
from PIL import Image
import io
import base64
from datetime import datetime
import tempfile
# PDF generation dependencies with error handling
try:
from reportlab.lib.pagesizes import letter, A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image as RLImage, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_LEFT
REPORTLAB_AVAILABLE = True
print("βœ… ReportLab library loaded successfully!")
except ImportError as e:
REPORTLAB_AVAILABLE = False
print(f"⚠️ ReportLab not available: {e}")
print("πŸ“ PDF generation will use client-side fallback only")
app = Flask(__name__)
app.secret_key = 'your-secret-key-here' # Change this to a secure secret key
# Configuration
UPLOAD_FOLDER = 'static/uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB max file size
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH
# Create upload directory if it doesn't exist
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs('static/css', exist_ok=True)
os.makedirs('static/js', exist_ok=True)
os.makedirs('templates', exist_ok=True)
@app.route('/uploads/<filename>')
def uploaded_file(filename):
"""Serve uploaded files"""
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
# Load the trained model
MODEL_PATH = "models/1.h5" # Update this path if needed
try:
model = tf.keras.models.load_model(MODEL_PATH)
print("βœ… Model loaded successfully!")
MODEL_LOADED = True
except Exception as e:
print(f"❌ Error loading model: {e}")
MODEL_LOADED = False
model = None
# Class names from your training (must match the exact order from training)
# Training order: ['Potato___Early_blight', 'Potato___Late_blight', 'Potato___healthy']
CLASS_NAMES = ["Potato___Early_blight", "Potato___Late_blight", "Potato___healthy"]
CLASS_DISPLAY_NAMES = ["Early Blight", "Late Blight", "Healthy"]
CLASS_DESCRIPTIONS = {
"Potato___Early_blight": "A common fungal disease that causes dark spots on potato leaves. Treatment with copper-based fungicides is recommended.",
"Potato___Late_blight": "A serious disease caused by Phytophthora infestans. Immediate action required - remove infected plants and apply appropriate fungicides.",
"Potato___healthy": "The potato plant appears healthy with no signs of disease detected. Continue good agricultural practices."
}
# Image preprocessing parameters
IMAGE_SIZE = 256
def allowed_file(filename):
"""Check if file extension is allowed"""
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def preprocess_image(image):
"""Preprocess the uploaded image for prediction"""
try:
# Convert to RGB if necessary
if image.mode != "RGB":
image = image.convert("RGB")
# Resize image
image = image.resize((IMAGE_SIZE, IMAGE_SIZE))
# Convert to numpy array
img_array = np.array(image)
# DO NOT normalize here - the model has built-in rescaling layer
# The model expects pixel values in range [0, 255]
# img_array = img_array / 255.0 # Removed this line
# Add batch dimension
img_array = np.expand_dims(img_array, axis=0)
# Debug: Print image statistics
print(f"Image shape: {img_array.shape}")
print(f"Image pixel range: [{img_array.min():.2f}, {img_array.max():.2f}]")
return img_array
except Exception as e:
print(f"Error preprocessing image: {e}")
return None
def predict_disease(image):
"""Make prediction on the preprocessed image"""
if not MODEL_LOADED or model is None:
return {"error": "Model not loaded"}
try:
# Preprocess image
processed_image = preprocess_image(image)
if processed_image is None:
return {"error": "Failed to preprocess image"}
# Make prediction
predictions = model.predict(processed_image)
predicted_class_index = np.argmax(predictions[0])
confidence = float(np.max(predictions[0]))
# Debug: Print prediction details
print(f"Raw predictions: {predictions[0]}")
print(f"Predicted class index: {predicted_class_index}")
print(f"Confidence: {confidence:.4f}")
# Get class name
predicted_class = CLASS_NAMES[predicted_class_index]
predicted_display_name = CLASS_DISPLAY_NAMES[predicted_class_index]
# Create detailed results
all_predictions = {}
for i, (class_name, display_name) in enumerate(zip(CLASS_NAMES, CLASS_DISPLAY_NAMES)):
all_predictions[display_name] = {
'probability': round(float(predictions[0][i]) * 100, 2),
'description': CLASS_DESCRIPTIONS[class_name]
}
return {
"predicted_class": predicted_display_name,
"confidence": round(confidence * 100, 2),
"description": CLASS_DESCRIPTIONS[predicted_class],
"all_predictions": all_predictions,
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
except Exception as e:
print(f"Prediction error: {e}")
return {"error": f"Prediction failed: {str(e)}"}
def get_recommendations(disease_name, confidence):
"""Get treatment recommendations based on prediction"""
recommendations = {
'Early Blight': [
"Remove affected leaves immediately and dispose properly",
"Apply copper-based fungicide spray",
"Improve air circulation around plants",
"Avoid overhead watering",
"Consider crop rotation for next season"
],
'Late Blight': [
"URGENT: Remove and destroy infected plants immediately",
"Apply systemic fungicides (metalaxyl-based)",
"Monitor weather conditions closely",
"Increase plant spacing for better air circulation",
"Harvest healthy tubers as soon as possible"
],
'Healthy': [
"Continue current care practices",
"Maintain proper watering schedule",
"Monitor plants regularly for early signs of disease",
"Ensure good soil drainage",
"Apply balanced fertilizer as needed"
]
}
return recommendations.get(disease_name, ["Consult agricultural expert for specific advice"])
@app.route('/')
def index():
"""Main page"""
return render_template('index.html', model_loaded=MODEL_LOADED)
@app.route('/test')
def test_upload():
"""Simple upload test page"""
return render_template('test_upload.html')
@app.route('/debug')
def debug_upload():
"""Debug upload test page"""
return render_template('debug_upload.html')
@app.route('/predict', methods=['POST'])
def predict():
"""Handle image upload and prediction"""
try:
print(f"Received prediction request. Files: {list(request.files.keys())}")
if 'file' not in request.files:
print("No file in request")
return jsonify({'error': 'No file uploaded'}), 400
file = request.files['file']
print(f"File received: {file.filename}, size: {file.content_length if hasattr(file, 'content_length') else 'unknown'}")
if file.filename == '':
print("Empty filename")
return jsonify({'error': 'No file selected'}), 400
if not allowed_file(file.filename):
print(f"Invalid file type: {file.filename}")
return jsonify({'error': 'Invalid file type. Please upload PNG, JPG, or JPEG files.'}), 400
# Ensure upload directory exists
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
# Save uploaded file
filename = secure_filename(file.filename)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{timestamp}_{filename}"
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
print(f"Saving file to: {filepath}")
file.save(filepath)
print(f"File saved successfully")
# Verify file exists
if not os.path.exists(filepath):
print(f"File was not saved properly: {filepath}")
return jsonify({'error': 'Failed to save uploaded file'}), 500
print(f"File size on disk: {os.path.getsize(filepath)} bytes")
# Open and predict
try:
image = Image.open(filepath)
print(f"Image opened successfully: {image.size}, mode: {image.mode}")
except Exception as e:
print(f"Failed to open image: {e}")
return jsonify({'error': f'Invalid image file: {str(e)}'}), 400
result = predict_disease(image)
print(f"Prediction result: {result}")
if 'error' in result:
return jsonify(result), 500
# Add recommendations and image URL for upload method
result['recommendations'] = get_recommendations(result['predicted_class'], result['confidence'])
result['image_url'] = url_for('uploaded_file', filename=filename)
print(f"Final result with image URL: {result['image_url']}")
return jsonify(result)
except Exception as e:
print(f"Prediction error: {e}")
import traceback
traceback.print_exc()
return jsonify({'error': f'Prediction failed: {str(e)}'}), 500
@app.route('/predict_camera', methods=['POST'])
def predict_camera():
"""Handle camera image prediction"""
try:
data = request.get_json()
if 'image' not in data:
return jsonify({'error': 'No image data provided'}), 400
# Decode base64 image
image_data = data['image'].split(',')[1] # Remove data:image/png;base64, prefix
image_bytes = base64.b64decode(image_data)
image = Image.open(io.BytesIO(image_bytes))
# Save camera image
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"camera_{timestamp}.png"
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
image.save(filepath)
# Make prediction
result = predict_disease(image)
if 'error' in result:
return jsonify(result), 500
# Add recommendations and image URL for camera method
result['recommendations'] = get_recommendations(result['predicted_class'], result['confidence'])
result['image_url'] = url_for('uploaded_file', filename=filename)
return jsonify(result)
except Exception as e:
return jsonify({'error': f'Camera prediction failed: {str(e)}'}), 500
@app.route('/health')
def health():
"""Health check endpoint"""
upload_dir_exists = os.path.exists(app.config['UPLOAD_FOLDER'])
upload_dir_writable = os.access(app.config['UPLOAD_FOLDER'], os.W_OK) if upload_dir_exists else False
return jsonify({
'status': 'healthy',
'model_loaded': MODEL_LOADED,
'upload_dir_exists': upload_dir_exists,
'upload_dir_writable': upload_dir_writable,
'upload_path': app.config['UPLOAD_FOLDER'],
'timestamp': datetime.now().isoformat()
})
@app.route('/debug/upload-test')
def debug_upload_test():
"""Debug endpoint to test upload directory"""
try:
# Ensure upload directory exists
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
# Test file creation
test_file = os.path.join(app.config['UPLOAD_FOLDER'], 'test.txt')
with open(test_file, 'w') as f:
f.write('test')
# Clean up test file
os.remove(test_file)
return jsonify({
'status': 'success',
'message': 'Upload directory is working correctly',
'path': app.config['UPLOAD_FOLDER']
})
except Exception as e:
return jsonify({
'status': 'error',
'message': f'Upload directory test failed: {str(e)}',
'path': app.config['UPLOAD_FOLDER']
}), 500
def test_model_predictions():
"""Test the model with some dummy data to verify it's working correctly"""
if not MODEL_LOADED or model is None:
return {"error": "Model not loaded"}
try:
# Create dummy test data - same shape as expected input
dummy_image = np.random.randint(0, 255, (1, IMAGE_SIZE, IMAGE_SIZE, 3), dtype=np.uint8)
# Make prediction
predictions = model.predict(dummy_image)
print(f"Model test - Input shape: {dummy_image.shape}")
print(f"Model test - Output shape: {predictions.shape}")
print(f"Model test - Predictions: {predictions[0]}")
print(f"Model test - Sum of predictions: {np.sum(predictions[0])}")
print(f"Model test - Class names order: {CLASS_NAMES}")
return {
"status": "success",
"input_shape": str(dummy_image.shape),
"output_shape": str(predictions.shape),
"predictions": predictions[0].tolist(),
"prediction_sum": float(np.sum(predictions[0])),
"class_names": CLASS_NAMES
}
except Exception as e:
print(f"Model test error: {e}")
return {"error": f"Model test failed: {str(e)}"}
@app.route('/debug/model-test')
def debug_model_test():
"""Debug endpoint to test model functionality"""
result = test_model_predictions()
return jsonify(result)
@app.errorhandler(413)
def too_large(e):
return jsonify({'error': 'File too large. Maximum size is 16MB.'}), 413
@app.errorhandler(404)
def not_found(e):
return render_template('index.html', model_loaded=MODEL_LOADED)
def generate_pdf_report(prediction_data, image_path=None):
"""Generate a professional PDF report for the disease prediction"""
if not REPORTLAB_AVAILABLE:
print("❌ ReportLab not available - cannot generate server-side PDF")
return None
try:
# Create a temporary file for the PDF
temp_pdf = tempfile.NamedTemporaryFile(delete=False, suffix='.pdf')
# Create the PDF document
doc = SimpleDocTemplate(temp_pdf.name, pagesize=A4)
styles = getSampleStyleSheet()
story = []
# Custom styles
title_style = ParagraphStyle(
'CustomTitle',
parent=styles['Title'],
fontSize=24,
spaceAfter=30,
alignment=TA_CENTER,
textColor=colors.darkgreen
)
heading_style = ParagraphStyle(
'CustomHeading',
parent=styles['Heading2'],
fontSize=16,
spaceAfter=12,
textColor=colors.darkblue
)
# Title
story.append(Paragraph("πŸ₯” POTATO DISEASE DETECTION REPORT", title_style))
story.append(Spacer(1, 20))
# Header info table
header_data = [
['Report Generated:', prediction_data.get('timestamp', datetime.now().strftime("%Y-%m-%d %H:%M:%S"))],
['Analysis Method:', 'Deep Learning AI Classification'],
['Model Version:', 'TensorFlow/Keras CNN v1.0']
]
header_table = Table(header_data, colWidths=[2*inch, 4*inch])
header_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), colors.lightgrey),
('TEXTCOLOR', (0, 0), (-1, -1), colors.black),
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
('FONTNAME', (0, 0), (-1, -1), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, -1), 10),
('BOTTOMPADDING', (0, 0), (-1, -1), 12),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
story.append(header_table)
story.append(Spacer(1, 30))
# Add image if provided
if image_path and os.path.exists(image_path):
story.append(Paragraph("πŸ“Έ ANALYZED IMAGE", heading_style))
try:
# Resize image to fit in PDF
img = RLImage(image_path)
img.drawHeight = 3 * inch
img.drawWidth = 3 * inch
story.append(img)
story.append(Spacer(1, 20))
except Exception as img_error:
print(f"Warning: Could not add image to PDF: {img_error}")
story.append(Paragraph("Image could not be embedded in PDF", styles['Normal']))
story.append(Spacer(1, 20))
# Diagnosis Section
story.append(Paragraph("🎯 DIAGNOSIS RESULTS", heading_style))
# Main diagnosis
diagnosis_data = [
['Predicted Disease:', prediction_data.get('predicted_class', 'Unknown')],
['Confidence Level:', f"{prediction_data.get('confidence', 0):.2f}%"],
['Risk Assessment:', get_risk_level(prediction_data.get('confidence', 0))]
]
diagnosis_table = Table(diagnosis_data, colWidths=[2*inch, 4*inch])
diagnosis_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), colors.lightblue),
('TEXTCOLOR', (0, 0), (-1, -1), colors.black),
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
('FONTNAME', (0, 0), (-1, -1), 'Helvetica'),
('FONTSIZE', (0, 0), (-1, -1), 12),
('BOTTOMPADDING', (0, 0), (-1, -1), 12),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
story.append(diagnosis_table)
story.append(Spacer(1, 20))
# Description
story.append(Paragraph("πŸ“‹ DESCRIPTION", heading_style))
description = Paragraph(prediction_data.get('description', 'No description available.'), styles['Normal'])
story.append(description)
story.append(Spacer(1, 20))
# Probability breakdown
story.append(Paragraph("πŸ“Š PROBABILITY BREAKDOWN", heading_style))
prob_data = [['Disease Type', 'Probability']]
all_predictions = prediction_data.get('all_predictions', {})
for disease, info in all_predictions.items():
prob_data.append([disease, f"{info.get('probability', 0):.2f}%"])
prob_table = Table(prob_data, colWidths=[3*inch, 2*inch])
prob_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
('FONTSIZE', (0, 0), (-1, -1), 10),
('BOTTOMPADDING', (0, 0), (-1, -1), 12),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
story.append(prob_table)
story.append(Spacer(1, 20))
# Recommendations
story.append(Paragraph("πŸ’‘ TREATMENT RECOMMENDATIONS", heading_style))
recommendations = prediction_data.get('recommendations', [])
for i, rec in enumerate(recommendations, 1):
rec_text = f"{i}. {rec}"
story.append(Paragraph(rec_text, styles['Normal']))
story.append(Spacer(1, 8))
story.append(Spacer(1, 30))
# Footer
footer_style = ParagraphStyle(
'Footer',
parent=styles['Normal'],
fontSize=10,
alignment=TA_CENTER,
textColor=colors.grey
)
story.append(Paragraph("_______________________________________________", footer_style))
story.append(Spacer(1, 10))
story.append(Paragraph("Generated by Potato Disease Detection System", footer_style))
story.append(Paragraph("Powered by Flask & TensorFlow | Lucky Sharma", footer_style))
story.append(Paragraph("Β© 2025 All Rights Reserved", footer_style))
# Build PDF
doc.build(story)
print(f"βœ… PDF report generated successfully: {temp_pdf.name}")
return temp_pdf.name
except Exception as e:
print(f"❌ Error generating PDF: {e}")
import traceback
traceback.print_exc()
return None
def get_risk_level(confidence):
"""Determine risk level based on confidence"""
if confidence >= 80:
return "High Confidence"
elif confidence >= 60:
return "Medium Confidence"
else:
return "Low Confidence - Manual Verification Recommended"
@app.route('/generate-pdf-report', methods=['POST'])
def generate_pdf_report_route():
"""Generate and download PDF report"""
try:
data = request.get_json()
if not data:
return jsonify({'error': 'No data provided'}), 400
# Check if ReportLab is available
if not REPORTLAB_AVAILABLE:
print("⚠️ ReportLab not available, suggesting client-side fallback")
return jsonify({
'error': 'Server-side PDF generation not available',
'fallback': 'client',
'message': 'ReportLab library not installed. Using client-side fallback.'
}), 503
# Get image path if provided
image_path = None
if 'image_url' in data:
# Extract filename from URL and construct full path
image_filename = data['image_url'].split('/')[-1]
image_path = os.path.join(app.config['UPLOAD_FOLDER'], image_filename)
# Verify image exists
if not os.path.exists(image_path):
image_path = None
# Generate PDF
pdf_path = generate_pdf_report(data, image_path)
if not pdf_path:
print("❌ PDF generation failed, suggesting client-side fallback")
return jsonify({
'error': 'Server-side PDF generation failed',
'fallback': 'client',
'message': 'Could not generate PDF on server. Using client-side fallback.'
}), 503
# Create filename
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
disease_name = data.get('predicted_class', 'unknown').replace(' ', '_')
pdf_filename = f"potato_disease_report_{disease_name}_{timestamp}.pdf"
print(f"βœ… PDF generated successfully: {pdf_filename}")
return send_file(
pdf_path,
as_attachment=True,
download_name=pdf_filename,
mimetype='application/pdf'
)
except Exception as e:
print(f"❌ PDF generation error: {e}")
import traceback
traceback.print_exc()
return jsonify({
'error': f'PDF generation failed: {str(e)}',
'fallback': 'client',
'message': 'Server error occurred. Using client-side fallback.'
}), 503
if __name__ == '__main__':
print("πŸš€ Starting Potato Disease Detection Flask App...")
print(f"πŸ“ Upload folder: {UPLOAD_FOLDER}")
print(f"πŸ€– Model loaded: {MODEL_LOADED}")
print("🌐 Access the app at: http://localhost:5000")
print("πŸ’‘ Press Ctrl+C to stop the server")
app.run(debug=True, host='0.0.0.0', port=5000)