import onnxruntime as ort import numpy as np from PIL import Image from torchvision import transforms import gradio as gr import cv2 from datetime import datetime, timedelta import os import html # Path to ONNX model (relative to repo root for HF Spaces) ONNX_PATH = "model/final_model.onnx" # Load ONNX runtime session ort_session = ort.InferenceSession(ONNX_PATH, providers=["CPUExecutionProvider"]) print("✅ ONNX model loaded") # Class labels (VERY IMPORTANT: order must match training) CLASS_NAMES = [ 'Bird-drop', 'Clean', 'Dusty', 'Electrical-damage', 'Physical-Damage', 'Snow-Covered' ] # Same preprocessing as training IMG_SIZE = 224 preprocess = transforms.Compose([ transforms.Resize((IMG_SIZE, IMG_SIZE)), transforms.Grayscale(num_output_channels=3), transforms.ToTensor(), transforms.Normalize( mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] ) ]) # Maintenance recommendations database MAINTENANCE_RECOMMENDATIONS = { 'Bird-drop': { 'severity': 'Medium', 'severity_color': '🟡', 'urgency': 'Schedule within 1-2 weeks', 'impact': '5-15% efficiency loss', 'actions': [ 'Clean affected panels with soft brush and water', 'Install bird deterrents (spikes, netting, or reflective tape)', 'Inspect for corrosion under droppings', 'Apply protective coating if acid damage detected' ], 'frequency': 'Inspect monthly in areas with high bird activity', 'degradation_rate': 0.8 }, 'Clean': { 'severity': 'Low', 'severity_color': '🟢', 'urgency': 'Routine maintenance only', 'impact': 'Optimal performance (0-2% below peak)', 'actions': [ 'Continue regular monitoring schedule', 'Quarterly visual inspections recommended', 'Annual professional inspection', 'Maintain vegetation clearance around panels' ], 'frequency': 'Quarterly inspections', 'degradation_rate': 0.5 }, 'Dusty': { 'severity': 'Medium', 'severity_color': '🟡', 'urgency': 'Schedule within 2-4 weeks', 'impact': '10-25% efficiency loss depending on dust thickness', 'actions': [ 'Clean panels with deionized water and soft microfiber cloth', 'Consider automated cleaning system for frequent dust', 'Apply anti-soiling nano-coating', 'Schedule cleaning before monsoon/rain season' ], 'frequency': 'Clean every 2-6 months (varies by location)', 'degradation_rate': 1.2 }, 'Electrical-damage': { 'severity': 'High', 'severity_color': '🔴', 'urgency': 'URGENT - Address within 24-48 hours', 'impact': '30-100% efficiency loss, fire/safety risk', 'actions': [ '⚠️ IMMEDIATELY disconnect affected panel circuit', 'Call certified solar technician for inspection', 'Check for loose connections, burnt wiring, or junction box damage', 'Perform thermographic scan of entire array', 'Replace damaged components (bypass diodes, connectors)', 'Test electrical continuity and insulation resistance' ], 'frequency': 'Emergency response, then quarterly electrical audits', 'degradation_rate': 5.0 }, 'Physical-Damage': { 'severity': 'High', 'severity_color': '🔴', 'urgency': 'URGENT - Address within 1 week', 'impact': '25-100% efficiency loss, water ingress risk', 'actions': [ 'Assess crack severity (micro-cracks vs. major breaks)', 'Seal minor cracks with UV-resistant clear sealant', 'Replace severely damaged panels', 'Check for moisture ingress in junction box', 'Inspect mounting hardware and structural integrity', 'Document damage for warranty/insurance claims' ], 'frequency': 'Immediate repair, then bi-annual structural inspections', 'degradation_rate': 3.5 }, 'Snow-Covered': { 'severity': 'Medium', 'severity_color': '🟡', 'urgency': 'Monitor and clear when safe', 'impact': '80-100% temporary efficiency loss (recovers after melting)', 'actions': [ 'Allow natural melting when possible (panels generate some heat)', 'Use soft snow rake with non-abrasive head if necessary', '⚠️ NEVER use hot water (thermal shock can crack panels)', 'Adjust panel tilt angle to 45°+ in snowy regions', 'Install heating cables for persistent snow areas', 'Clear bottom panels first to enable snow sliding' ], 'frequency': 'As needed during winter months', 'degradation_rate': 0.0 } } def predict_degradation(defect_class, current_efficiency=100, time_horizon_months=12): """ Predict solar panel efficiency degradation over time """ maintenance = MAINTENANCE_RECOMMENDATIONS[defect_class] degradation_rate = maintenance['degradation_rate'] timeline = [] efficiency = current_efficiency if defect_class in ['Electrical-damage', 'Physical-Damage']: for week in range(0, min(time_horizon_months * 4, 52), 2): date = datetime.now() + timedelta(weeks=week) timeline.append({ 'date': date.strftime('%b %d, %Y'), 'efficiency': max(0, efficiency), 'status': '🔴 Critical' if efficiency < 50 else '🟡 Degraded' }) efficiency -= degradation_rate else: for month in range(0, time_horizon_months + 1, 2): date = datetime.now() + timedelta(days=month * 30) timeline.append({ 'date': date.strftime('%b %d, %Y'), 'efficiency': max(0, efficiency), 'status': '🟢 Good' if efficiency > 85 else '🟡 Fair' if efficiency > 70 else '🔴 Poor' }) efficiency -= degradation_rate return timeline def format_maintenance_report(defect_class, confidence): """ Generate comprehensive maintenance report """ maint = MAINTENANCE_RECOMMENDATIONS[defect_class] actions_formatted = '\n'.join([f'**{i+1}.** {action}' for i, action in enumerate(maint['actions'])]) report = f"""

🔧 Maintenance Report

📋 Detection Summary

Condition Detected: {defect_class}

Confidence Level: {confidence*100:.1f}%

Severity: {maint['severity_color']} {maint['severity']}

⏰ Urgency: {maint['urgency']}

📊 Performance Impact

{maint['impact']}

✅ Recommended Actions

{actions_formatted}

📅 Maintenance Frequency: {maint['frequency']}

⚠️ Degradation Rate: {maint['degradation_rate']}% efficiency loss per {'week' if defect_class in ['Electrical-damage'] else 'month'} if untreated

""" return report def format_degradation_prediction(timeline): """ Format degradation prediction timeline """ table_rows = '\n'.join([ f"{entry['date']}" f"{entry['efficiency']:.1f}%" f"{entry['status']}" for entry in timeline ]) report = f"""

📉 Degradation Forecast

Projected efficiency without maintenance intervention

{table_rows}
Date Efficiency Status

💡 Prevention Strategies

⚡ Performance Optimization Tips

""" return report def get_gradcam_heatmap(pil_image, pred_idx): img = preprocess(pil_image).unsqueeze(0).numpy().astype(np.float32) outputs = ort_session.run(None, {"input_image": img})[0] img_array = np.array(pil_image.resize((IMG_SIZE, IMG_SIZE))) if len(img_array.shape) == 2: img_array = np.stack([img_array] * 3, axis=-1) gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY) edges = cv2.Canny(gray, 50, 150) heatmap = cv2.GaussianBlur(edges.astype(np.float32), (21, 21), 0) if heatmap.max() > 0: heatmap = heatmap / heatmap.max() return heatmap def create_heatmap_overlay(pil_image, heatmap): img_array = np.array(pil_image.resize((IMG_SIZE, IMG_SIZE))) if len(img_array.shape) == 2: img_array = np.stack([img_array] * 3, axis=-1) heatmap_colored = cv2.applyColorMap( (heatmap * 255).astype(np.uint8), cv2.COLORMAP_JET ) heatmap_colored = cv2.cvtColor(heatmap_colored, cv2.COLOR_BGR2RGB) overlay = cv2.addWeighted(img_array, 0.6, heatmap_colored, 0.4, 0) return Image.fromarray(overlay.astype(np.uint8)) def predict_image(pil_image): img = preprocess(pil_image).unsqueeze(0).numpy().astype(np.float32) outputs = ort_session.run(None, {"input_image": img})[0] exp_scores = np.exp(outputs) probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True) probs = probs[0] pred_idx = int(np.argmax(probs)) predicted_class = CLASS_NAMES[pred_idx] confidence = float(probs[pred_idx]) prob_dict = {CLASS_NAMES[i]: float(probs[i]) for i in range(len(CLASS_NAMES))} return predicted_class, confidence, prob_dict, pred_idx def extract_frame_from_video(video_path): """ Extract the middle frame from a video file as a PIL Image. Used as a fallback when the user uploads a video instead of an image. """ cap = cv2.VideoCapture(video_path) if not cap.isOpened(): raise ValueError("Could not open the uploaded video file.") total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) mid_frame = total_frames // 2 cap.set(cv2.CAP_PROP_POS_FRAMES, mid_frame) ret, frame = cap.read() cap.release() if not ret: raise ValueError("Could not read a frame from the uploaded video.") frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) return Image.fromarray(frame_rgb) print("✅ Inference pipeline ready") def gradio_predict(image, video, current_efficiency, time_horizon): # Video fallback: if no image provided but a video is, extract its middle frame if image is None and video is not None: try: image = extract_frame_from_video(video) except Exception as e: error_msg = f"

⚠️ Could not process video: {html.escape(str(e))}

" return None, None, None, None, error_msg, "" if image is None: return None, None, None, None, "

⚠️ Please upload an image or video to analyze.

", "" pred_class, confidence, prob_dict, pred_idx = predict_image(image) heatmap = get_gradcam_heatmap(image, pred_idx) heatmap_overlay = create_heatmap_overlay(image, heatmap) maintenance_report = format_maintenance_report(pred_class, confidence) timeline = predict_degradation(pred_class, current_efficiency, int(time_horizon)) degradation_report = format_degradation_prediction(timeline) return pred_class, f"{confidence * 100:.2f}%", prob_dict, heatmap_overlay, maintenance_report, degradation_report # Custom CSS for dark neutral theme custom_css = """ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); .gradio-container { font-family: 'Inter', sans-serif !important; } .main-header { text-align: center; background: linear-gradient(135deg, #525252 0%, #404040 100%); padding: 40px; border-radius: 15px; color: #fafafa; margin-bottom: 30px; border: 1px solid #404040; } .upload-section { background: #171717; padding: 25px; border-radius: 12px; border: 1px solid #262626; } .dark { background-color: #0a0a0a !important; } /* Override Gradio's default backgrounds */ .gr-box, .gr-form, .gr-panel { background-color: #171717 !important; border-color: #262626 !important; } .gr-input, .gr-text-input { background-color: #0a0a0a !important; border-color: #404040 !important; color: #fafafa !important; } .gr-button { background: linear-gradient(135deg, #525252 0%, #404040 100%) !important; color: #fafafa !important; border: 1px solid #404040 !important; } .gr-button:hover { background: linear-gradient(135deg, #737373 0%, #525252 100%) !important; } label { color: #d4d4d4 !important; } .gr-prose { color: #d4d4d4 !important; } """ with gr.Blocks(css=custom_css, theme=gr.themes.Default(primary_hue="neutral", secondary_hue="neutral")) as iface: gr.HTML("""

☀️ Solar Panel AI Diagnostics

Intelligent defect detection, maintenance planning & performance forecasting

""") with gr.Row(): with gr.Column(scale=1): gr.HTML('
') input_image = gr.Image( type="pil", label="📸 Upload Solar Panel Image", height=300 ) input_video = gr.Video( label="🎬 Or Upload a Video (fallback — middle frame will be used)", height=300 ) gr.HTML('
') with gr.Row(): current_eff = gr.Slider( minimum=50, maximum=100, value=95, step=1, label="⚡ Current System Efficiency (%)", info="Set your panel's current performance level" ) with gr.Row(): time_horiz = gr.Slider( minimum=3, maximum=24, value=12, step=3, label="📅 Forecast Period (months)", info="Choose prediction time horizon" ) predict_btn = gr.Button( "🔍 Analyze Solar Panel", variant="primary", size="lg", scale=1 ) with gr.Column(scale=1): with gr.Group(): pred_class = gr.Textbox( label="🎯 Detected Condition", interactive=False, container=True ) confidence = gr.Textbox( label="📊 Confidence Score", interactive=False, container=True ) prob_dist = gr.Label( label="📈 Classification Probabilities", num_top_classes=6 ) heatmap_img = gr.Image( type="pil", label="🔥 AI Attention Heatmap", height=300 ) with gr.Row(): with gr.Column(): maintenance_output = gr.HTML(label="Maintenance Report") with gr.Row(): with gr.Column(): degradation_output = gr.HTML(label="Degradation Forecast") predict_btn.click( fn=gradio_predict, inputs=[input_image, input_video, current_eff, time_horiz], outputs=[pred_class, confidence, prob_dist, heatmap_img, maintenance_output, degradation_output] ) gr.HTML("""

📖 How to Use This System

1️⃣ Upload Image or Video

Upload a photo of your solar panel (thermal, infrared, or RGB). You can also upload a short video — the middle frame will be extracted and analyzed automatically.

2️⃣ Set Parameters

Adjust current efficiency and forecast timeframe

3️⃣ Analyze

Click analyze to get comprehensive diagnostics

4️⃣ Review & Act

Check maintenance actions and follow recommendations

⚡ Detection Capabilities

This AI system detects 6 types of solar panel conditions: Bird droppings, Clean panels, Dust accumulation, Electrical damage, Physical damage, and Snow coverage. The attention heatmap visualizes which areas influenced the AI's decision-making process.

""") if __name__ == "__main__": iface.launch()