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('
Real Computer Vision + AI Analysis
AI Assistant - India