import streamlit as st import base64 import requests import json import io import re from PIL import Image st.set_page_config(page_title="Solar Rooftop Analyzer", layout="centered") st.title("\U0001F31E Solar Rooftop Analysis") st.markdown("Upload a rooftop image and provide your location and budget. The system will analyze the rooftop and estimate potential solar installation ROI.") OPENROUTER_API_KEY = "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" VISION_MODEL_NAME = "opengvlab/internvl3-14b:free" def analyze_image_with_openrouter(image_file): # Read and convert image to JPEG bytes img = Image.open(image_file).convert("RGB") buffer = io.BytesIO() img.save(buffer, format="JPEG") jpeg_bytes = buffer.getvalue() # Base64 encode with content-type prefix encoded_image = "data:image/jpeg;base64," + base64.b64encode(jpeg_bytes).decode("utf-8") prompt = ( "Analyze the rooftop in this image. Output JSON with: " "[Roof area (sqm), Sunlight availability (%), Shading (Yes/No), " "Recommended solar panel type, Estimated capacity (kW)]." ) headers = { "Authorization": f"Bearer {OPENROUTER_API_KEY}", "Content-Type": "application/json" } payload = { "model": VISION_MODEL_NAME, "messages": [ { "role": "user", "content": [ {"type": "text", "text": prompt}, {"type": "image_url", "image_url": {"url": encoded_image}} ] } ] } response = requests.post("https://openrouter.ai/api/v1/chat/completions", json=payload, headers=headers) if response.status_code == 200: return response.json() return {"error": f"Failed to analyze image. Status code: {response.status_code}, Response: {response.text}"} def extract_json_from_response(content): try: match = re.search(r"\{.*\}", content, re.DOTALL) if match: return json.loads(match.group(0)) except Exception as e: st.warning(f"Failed to parse JSON: {e}") return None def estimate_roi(roof_area, capacity_kw, budget): cost_per_kw = 65000 # INR/kW estimated_cost = capacity_kw * cost_per_kw incentives = estimated_cost * 0.30 net_cost = estimated_cost - incentives annual_savings = capacity_kw * 1500 * 7 payback_years = round(net_cost / annual_savings, 2) return { "estimated_cost": estimated_cost, "incentives": incentives, "net_cost": net_cost, "annual_savings": annual_savings, "payback_years": payback_years, "within_budget": budget >= net_cost } with st.form("solar_form"): uploaded_file = st.file_uploader("Upload Rooftop Image", type=["jpg", "jpeg", "png"]) location = st.text_input("Location") budget = st.number_input("Budget (INR)", min_value=10000.0, step=1000.0) submitted = st.form_submit_button("Analyze") if submitted: if uploaded_file and location and budget: st.image(uploaded_file, caption="Uploaded Rooftop Image", use_column_width=True) with st.spinner("Analyzing rooftop image..."): ai_response = analyze_image_with_openrouter(uploaded_file) if "choices" in ai_response: try: content = ai_response["choices"][0]["message"]["content"] content_json = extract_json_from_response(content) if content_json: st.success("Analysis complete!") st.subheader("Rooftop Analysis") st.json(content_json) if "Roof area (sqm)" in content_json and "Estimated capacity (kW)" in content_json: roi = estimate_roi( roof_area=content_json["Roof area (sqm)"], capacity_kw=content_json["Estimated capacity (kW)"], budget=budget ) st.subheader("ROI Estimation") st.json(roi) else: st.error("Could not extract structured data from the AI response.") st.text(content) # Fallback: show raw content except Exception as e: st.error(f"Error parsing analysis content: {e}") st.json(ai_response) else: st.error("Failed to analyze the image. Please try again.") else: st.warning("Please upload an image and fill all fields.")