import streamlit as st import os from dotenv import load_dotenv from openai import OpenAI import base64 import json import time from datetime import datetime import cv2 import numpy as np from PIL import Image load_dotenv() class SolarAnalyzer: def __init__(self): self.client = OpenAI( base_url="https://openrouter.ai/api/v1", api_key=os.getenv("OPENROUTER_API_KEY", ""), default_headers={"HTTP-Referer": "http://localhost:8501", "X-Title": "Solar Analyzer"} ) self.models = { "Qwen 2.5 VL 72B": "qwen/qwen2.5-vl-72b-instruct:free", "Qwen 2.5 VL 32B": "qwen/qwen2.5-vl-32b-instruct:free", "Qwen 2.5 VL 3B": "qwen/qwen2.5-vl-3b-instruct:free" } self.PANEL_WATTAGE, self.COST_PER_WATT, self.TAX_CREDIT = 400, 200, 0.30 self.SUN_HOURS, self.ELECTRICITY_RATE = 1800, 8 def analyze_image_with_cv(self, uploaded_file): start_time = time.time() try: image = Image.open(uploaded_file) img_array = np.array(image) img_cv = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) if len(img_array.shape) == 3 else img_array height, width = img_cv.shape[:2] gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY) laplacian_var = cv2.Laplacian(gray, cv2.CV_64F).var() brightness, contrast = np.mean(gray), np.std(gray) if laplacian_var > 800 and contrast > 50 and brightness > 130: condition = "excellent" condition_multiplier = 1.0 elif laplacian_var > 500 and contrast > 40: condition = "excellent" if brightness > 120 else "good" condition_multiplier = 0.95 if brightness > 120 else 0.85 elif laplacian_var > 300 and contrast > 30: condition = "good" condition_multiplier = 0.80 elif laplacian_var > 150 and contrast > 20: condition = "fair" condition_multiplier = 0.65 else: condition = "poor" condition_multiplier = 0.50 blurred = cv2.GaussianBlur(gray, (5, 5), 0) thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) total_area = height * width significant_contours = [c for c in contours if cv2.contourArea(c) > total_area * 0.005] roof_area = sum(cv2.contourArea(c) for c in significant_contours) base_usable = (roof_area / total_area) * 100 brightness_factor = min(brightness / 128.0, 1.2) contrast_factor = min(contrast / 40.0, 1.1) sharpness_factor = min(laplacian_var / 500.0, 1.1) usable_percent = base_usable * brightness_factor * contrast_factor * sharpness_factor * condition_multiplier usable_percent = max(min(usable_percent, 90), 15) # Clamp between 15-90% pixel_density = (width * height) / 1000000 # Megapixels if pixel_density > 2.0: # High resolution image area_multiplier = 1.2 elif pixel_density > 1.0: # Medium resolution area_multiplier = 1.0 else: # Low resolution area_multiplier = 0.8 estimated_roof_area_m2 = (usable_percent / 100) * area_multiplier * (50 + (total_area / 50000)) panel_area = 1.65 # m² per panel max_panels = int(estimated_roof_area_m2 / panel_area) system_kw = max_panels * 0.4 # 400W per panel if condition == "excellent": system_kw *= 1.1 elif condition == "poor": system_kw *= 0.7 system_kw = max(min(system_kw, 20), 2) confidence = 40 # Sharpness contribution (0-30 points) if laplacian_var > 800: confidence += 30 elif laplacian_var > 500: confidence += 25 elif laplacian_var > 200: confidence += 15 elif laplacian_var > 100: confidence += 8 # Brightness contribution (0-20 points) if 80 < brightness < 180: confidence += 20 elif 60 < brightness < 200: confidence += 12 elif 40 < brightness < 220: confidence += 5 # Contrast contribution (0-15 points) if contrast > 50: confidence += 15 elif contrast > 40: confidence += 12 elif contrast > 25: confidence += 8 elif contrast > 15: confidence += 4 if total_area > 1000000: confidence += 10 elif total_area > 500000: confidence += 6 elif total_area > 200000: confidence += 3 confidence = min(confidence, 95) analysis_time = time.time() - start_time return { "success": True, "data": { "roof_condition": condition, "usable_area_percent": int(usable_percent), "system_size_kw": round(system_kw, 1), "confidence": int(confidence), "notes": f"{condition.title()} roof, {int(usable_percent)}% usable area, {int(confidence)}% confidence", "analysis_time": round(analysis_time, 2), "image_size": f"{width}x{height}", "image_metrics": { "brightness": round(brightness, 1), "contrast": round(contrast, 1), "sharpness": round(laplacian_var, 1), "roof_area_m2": round(estimated_roof_area_m2, 1) } } } except Exception as e: return {"success": False, "error": str(e)} def enhance_with_ai(self, image_base64, cv_analysis, model_name): start_time = time.time() try: response = self.client.chat.completions.create( model=self.models[model_name], messages=[{ "role": "user", "content": [ {"type": "text", "text": f"Enhance this roof analysis: {cv_analysis}. Return JSON with shading_assessment and roof_orientation."}, {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_base64}"}} ] }], max_tokens=300 ) result = json.loads(response.choices[0].message.content) result["ai_time"] = round(time.time() - start_time, 2) return {"success": True, "data": {**cv_analysis, **result}} except: return {"success": True, "data": {**cv_analysis, "shading_assessment": "moderate", "roof_orientation": "good"}} def calculate_metrics(self, system_kw): annual_production = int(system_kw * self.SUN_HOURS * 0.8) gross_cost = int(system_kw * 1000 * self.COST_PER_WATT) net_cost = int(gross_cost * (1 - self.TAX_CREDIT)) annual_savings = int(annual_production * self.ELECTRICITY_RATE) payback_years = round(net_cost / annual_savings if annual_savings > 0 else 0, 1) lifetime_savings = int((annual_savings * 25) - net_cost) panels_needed = int((system_kw * 1000) / self.PANEL_WATTAGE) return { "system_kw": system_kw, "panels": panels_needed, "annual_kwh": annual_production, "gross_cost": gross_cost, "net_cost": net_cost, "annual_savings": annual_savings, "payback_years": payback_years, "lifetime_savings": lifetime_savings, "co2_offset": round(annual_production * 0.0004, 1) } def format_inr(amount): if amount <= 0: return "₹0" s = str(int(amount)) if len(s) <= 3: return "₹" + s last3, rest = s[-3:], s[:-3] parts = [] while len(rest) > 2: parts.append(rest[-2:]) rest = rest[:-2] if rest: parts.append(rest) parts.reverse() return "₹" + ",".join(parts) + "," + last3 if parts else "₹" + last3 def main(): st.set_page_config(page_title="Solar Analyzer - India", page_icon="☀️", layout="wide") # CSS for proper display st.markdown(""" """, unsafe_allow_html=True) st.markdown('

☀️ Solar Rooftop Analyzer - India

Real Computer Vision + AI Analysis

', unsafe_allow_html=True) analyzer = SolarAnalyzer() with st.sidebar: st.markdown("""

☀️ Solar Industry

AI Assistant - India

""", unsafe_allow_html=True) st.header("⚙️ Settings") api_key_status = os.getenv("OPENROUTER_API_KEY") if api_key_status: st.success("✅ API Key Found") else: st.error("❌ Add API Key") model = st.selectbox("AI Model", list(analyzer.models.keys())) max_size = st.slider("Max System Size (kW)", 1, 20, 15) use_ai = st.checkbox("Use AI Enhancement", value=True) st.info("Free tier: 50 requests/day") st.markdown("### 🇮🇳 Indian Context") st.write("• **Rate**: ₹8/kWh") st.write("• **Cost**: ₹200/W") st.write("• **Subsidy**: 30%") st.write("• **Sun Hours**: 1800/year") col1, col2 = st.columns([1, 1]) with col1: st.subheader("Upload Rooftop Image") uploaded_file = st.file_uploader("Choose rooftop image", type=['png', 'jpg', 'jpeg']) if uploaded_file: st.image(uploaded_file, caption="Rooftop Image", use_container_width=True) if st.button("🔍 Analyze with Computer Vision", type="primary"): analysis_start = time.time() with st.spinner("Analyzing..."): cv_result = analyzer.analyze_image_with_cv(uploaded_file) if cv_result["success"]: cv_analysis = cv_result["data"] if use_ai and api_key_status: with st.spinner("AI Enhancement..."): image_base64 = base64.b64encode(uploaded_file.getvalue()).decode() ai_result = analyzer.enhance_with_ai(image_base64, cv_analysis, model) final_analysis = ai_result["data"] if ai_result["success"] else cv_analysis else: final_analysis = cv_analysis system_kw = min(final_analysis["system_size_kw"], max_size) metrics = analyzer.calculate_metrics(system_kw) total_time = time.time() - analysis_start final_analysis["total_analysis_time"] = round(total_time, 2) st.session_state.analysis = final_analysis st.session_state.metrics = metrics st.session_state.completed = True st.session_state.model_used = model st.success(f"✅ Analysis Complete in {total_time:.2f}s!") st.info(f"🤖 Method: {'CV + AI' if use_ai else 'CV Only'}") else: st.error(f"❌ Failed: {cv_result.get('error')}") else: st.info("**Features:**\n- 🔬 Computer Vision\n- 🤖 AI Enhancement\n- 📊 Dynamic Results\n- 🇮🇳 Indian Context") with col2: st.subheader("Analysis Results") if hasattr(st.session_state, 'completed') and st.session_state.completed: analysis = st.session_state.analysis metrics = st.session_state.metrics with st.expander("📊 Summary", expanded=True): col_a, col_b, col_c = st.columns(3) with col_a: st.metric("🏠 Roof", analysis["roof_condition"].title()) st.metric("⚡ Size", f"{metrics['system_kw']}kW") st.metric("📊 Annual", f"{metrics['annual_kwh']//1000}k kWh") with col_b: cost_lakhs = metrics['net_cost'] / 100000 savings_k = metrics['annual_savings'] / 1000 st.metric("💰 Cost", f"₹{cost_lakhs:.1f}L") st.metric("💵 Save/yr", f"₹{savings_k:.0f}k") st.metric("⏱️ Payback", f"{metrics['payback_years']}yr") with col_c: roi = int((metrics['lifetime_savings']/metrics['net_cost'])*100) if metrics['net_cost'] > 0 else 0 total_lakhs = metrics['lifetime_savings'] / 100000 st.metric("📈 ROI", f"{roi}%") st.metric("💎 Lifetime", f"₹{total_lakhs:.1f}L") st.metric("🌱 CO₂/yr", f"{metrics['co2_offset']}t") # Recommendations condition, payback = analysis["roof_condition"].lower(), metrics['payback_years'] if condition == "excellent" and payback < 8: st.success("✅ Excellent investment opportunity!") elif condition in ["good", "excellent"] and payback < 12: st.info("👍 Good solar potential") elif condition in ["good", "excellent"] and payback < 15: st.warning("⚠️ Good roof but longer payback - still viable") else: st.info("📊 Viable investment - consider efficiency improvements") with st.expander("⚡ Performance Metrics"): perf_col1, perf_col2, perf_col3 = st.columns(3) with perf_col1: st.metric("CV Time", f"{analysis.get('analysis_time', 0):.2f}s") st.metric("Total Time", f"{analysis.get('total_analysis_time', 0):.2f}s") with perf_col2: st.metric("Confidence", f"{analysis['confidence']}%") st.metric("Image Size", analysis.get('image_size', 'N/A')) with perf_col3: st.metric("Model", st.session_state.model_used[:10] + "...") if 'ai_time' in analysis: st.metric("AI Time", f"{analysis['ai_time']:.2f}s") with st.expander("🔧 Technical Details"): st.write(f"**Condition**: {analysis['roof_condition'].title()}") st.write(f"**Usable Area**: {analysis['usable_area_percent']}%") st.write(f"**Panels**: {metrics['panels']} | **Confidence**: {analysis['confidence']}%") st.write(f"**Notes**: {analysis['notes']}") if 'shading_assessment' in analysis: st.write(f"**Shading**: {analysis['shading_assessment'].title()}") with st.expander("💰 Financial Breakdown"): st.write(f"**Gross**: {format_inr(metrics['gross_cost'])} | **Subsidy**: {format_inr(metrics['gross_cost'] - metrics['net_cost'])}") st.write(f"**Net**: {format_inr(metrics['net_cost'])} | **Annual**: {format_inr(metrics['annual_savings'])}") st.write(f"**25-Year Savings**: {format_inr(metrics['lifetime_savings'])}") # Download st.markdown("---") report_data = { "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "performance": {"total_time": analysis.get('total_analysis_time'), "confidence": analysis['confidence']}, "analysis": analysis, "metrics": metrics, "currency": "INR" } st.download_button("📄 Download Report", data=json.dumps(report_data, indent=2), file_name=f"solar_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json", mime="application/json") else: st.info("Upload an image to start analysis") st.markdown("---") st.markdown("
Solar Industry AI Assistant - India Edition
Real CV Analysis • Supporting India's Renewable Energy Goals
", unsafe_allow_html=True) if __name__ == "__main__": main()