import streamlit as st import cv2 import numpy as np from PIL import Image def process_rooftop_image(image, geographic_bounds): """ Process the rooftop image to detect rooftops, exclude shadows and waste areas, and calculate usable area for solar panels. Parameters: image (numpy array): Input image as a NumPy array. geographic_bounds (dict): Geographic bounds with the following keys: - lat_min, lat_max, lon_min, lon_max (floats): Latitude and longitude bounds of the image. Returns: final_image (numpy array): Image with predicted boundaries and excluded shadows. usable_area (float): Computed rooftop area usable for solar panels in square meters. """ # Convert the image to grayscale gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Apply Gaussian Blur to reduce noise blurred = cv2.GaussianBlur(gray, (5, 5), 0) # Shadow detection (thresholding for dark regions) _, shadow_mask = cv2.threshold(blurred, 80, 255, cv2.THRESH_BINARY_INV) # Edge detection (Canny) edges = cv2.Canny(blurred, 50, 150) # Morphological operations to close gaps in edges kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) closed_edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel) # Find contours of the rooftop contours, _ = cv2.findContours(closed_edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # Create a blank mask for the rooftop area rooftop_mask = np.zeros_like(gray) for contour in contours: # Approximate the contour and filter out unwanted small areas approx = cv2.approxPolyDP(contour, 0.02 * cv2.arcLength(contour, True), True) area = cv2.contourArea(approx) if area > 500: # Minimum area threshold to remove noise cv2.drawContours(rooftop_mask, [approx], -1, 255, -1) # Exclude shadows from the rooftop mask final_mask = cv2.bitwise_and(rooftop_mask, cv2.bitwise_not(shadow_mask)) # Calculate the area in pixels pixel_area = np.sum(final_mask > 0) # Convert pixels to real-world area lat_min, lat_max, lon_min, lon_max = ( geographic_bounds['lat_min'], geographic_bounds['lat_max'], geographic_bounds['lon_min'], geographic_bounds['lon_max'], ) lat_diff = abs(lat_max - lat_min) lon_diff = abs(lon_max - lon_min) # Assuming Earth radius = 6371 km earth_radius = 6371000 # in meters lat_conversion = (lat_diff * np.pi / 180) * earth_radius lon_conversion = (lon_diff * np.pi / 180) * earth_radius * np.cos(lat_min * np.pi / 180) # Conversion factor: real-world area per pixel conversion_factor = (lat_conversion * lon_conversion) / (image.shape[0] * image.shape[1]) usable_area = pixel_area * conversion_factor # in square meters # Overlay contours on the original image final_image = image.copy() cv2.drawContours(final_image, contours, -1, (0, 255, 0), 2) return final_image, usable_area # Streamlit Interface st.title("Rooftop Usable Area for Solar Panels") st.write("Upload a rooftop image to calculate the usable area for solar panels, panel count, total cost, and energy generation potential.") # File uploader for the image uploaded_file = st.file_uploader("Upload an image (PNG or JPG)", type=["png", "jpg", "jpeg"]) # Input fields for geographic bounds with default values lat_min = st.number_input("Latitude Min", value=89.67, step=0.01) lat_max = st.number_input("Latitude Max", value=89.68, step=0.01) lon_min = st.number_input("Longitude Min", value=90.78, step=0.01) lon_max = st.number_input("Longitude Max", value=90.79, step=0.01) # Input fields for irradiance, panel efficiency, and sunlight hours irradiance = st.number_input("Irradiance (W/m²)", value=200, step=1) # Default irradiance value (W/m²) panel_efficiency = st.number_input("Panel Efficiency (%)", value=18, step=1) / 100 # Default efficiency (18%) sunlight_hours = st.number_input("Sunlight Hours per Day", value=7, step=1) # Default 5 hours of sunlight # Button to process the image if st.button("Calculate Usable Area and Energy Potential"): if uploaded_file is not None: # Read the uploaded image image = np.array(Image.open(uploaded_file).convert("RGB")) # Convert PIL image to NumPy array image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) # Convert RGB to BGR for OpenCV geographic_bounds = { 'lat_min': lat_min, 'lat_max': lat_max, 'lon_min': lon_min, 'lon_max': lon_max, } # Process the image final_image, usable_area = process_rooftop_image(image, geographic_bounds) # Calculate panel count, total cost, and potential energy panel_area = 5 * 5 # Each panel covers 25 square meters panel_cost = 25000 # Cost per panel in INR panel_count = usable_area // panel_area total_cost = panel_count * panel_cost # Calculate potential energy generation in kWh energy_generation_kWh = (usable_area * panel_efficiency * irradiance * sunlight_hours) / 1000 # in kWh # Display results st.image(cv2.cvtColor(final_image, cv2.COLOR_BGR2RGB), caption="Processed Image with Boundaries") st.write(f"Usable Rooftop Area for Solar Panels (excluding shadows): **{usable_area:.2f} square meters**") st.write(f"Number of 5×5m panels that can be used: **{int(panel_count)}**") st.write(f"Total installation cost (₹25,000 per panel): **₹{int(total_cost):,}**") st.write(f"Total potential energy generation per day: **{energy_generation_kWh:.2f} kWh**") else: st.error("Please upload an image to proceed.")