panelspecifier / app.py
Harsh-7300's picture
Update app.py
d693401 verified
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.")