Spaces:
Sleeping
Sleeping
File size: 4,749 Bytes
5c52fb9 66bcd8c 5c52fb9 66bcd8c 5c52fb9 9096a3a 5c52fb9 4f26dc9 5c52fb9 9096a3a 5c52fb9 66bcd8c 5c52fb9 e6f2efc 5c52fb9 eb2a5d2 5c52fb9 eb2a5d2 5c52fb9 80940c5 5c52fb9 eb2a5d2 5c52fb9 80940c5 5c52fb9 5ad10c2 eb2a5d2 5c52fb9 80940c5 5c52fb9 80940c5 eb2a5d2 5c52fb9 eb2a5d2 5c52fb9 eb2a5d2 5c52fb9 80940c5 66bcd8c 5c52fb9 66bcd8c 5c52fb9 eb2a5d2 80940c5 5c52fb9 eb2a5d2 5c52fb9 66bcd8c 5c52fb9 eb2a5d2 80940c5 5c52fb9 73fbf43 5c52fb9 80940c5 5c52fb9 66bcd8c 5c52fb9 66bcd8c 5c52fb9 80940c5 66bcd8c 5c52fb9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | """RoofSight — Roof Plane Segmentation
Gradio web app using NVIDIA C-RADIOv4-H (631M params) for zero-shot
roof type segmentation combined with RANSAC 3D plane fitting on
Google Solar API DSM data.
Deployed on HuggingFace Spaces with ZeroGPU.
"""
import json
import os
import tempfile
import gradio as gr
import numpy as np
import torch
# Pre-load model weights to CPU at startup (moved to GPU per-request by ZeroGPU)
from radio_backbone import load_model
print("Initializing RoofSight...")
device = "cuda" if torch.cuda.is_available() else "cpu"
load_model(device="cpu")
# Try importing spaces for ZeroGPU decorator
try:
import spaces
GPU_DECORATOR = spaces.GPU
except ImportError:
# Local dev without ZeroGPU
def GPU_DECORATOR(fn):
return fn
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY", "")
@GPU_DECORATOR
def process(address, api_key, radius, ransac_threshold, max_planes, min_area):
"""Run the full pipeline and return results for Gradio."""
from pipeline import run as run_pipeline
api_key = api_key.strip() if api_key.strip() else GOOGLE_API_KEY
if not api_key:
return None, None, "", "No API key provided. Enter your Google Solar API key or set GOOGLE_API_KEY."
compute_device = "cuda" if torch.cuda.is_available() else "cpu"
try:
result = run_pipeline(
address=address,
api_key=api_key,
radius_meters=int(radius),
ransac_threshold=ransac_threshold,
max_planes=int(max_planes),
min_area_sqft=min_area,
device=compute_device,
)
geojson_str = json.dumps(result.geojson, indent=2)
status = "\n".join(result.status)
return result.original_image, result.overlay, geojson_str, status
except Exception as e:
return None, None, "", f"Error: {str(e)}"
def save_geojson(geojson_str):
"""Save GeoJSON to a downloadable temp file."""
if not geojson_str or geojson_str.strip() == "":
return None
tmp = tempfile.NamedTemporaryFile(suffix=".geojson", delete=False, mode="w")
tmp.write(geojson_str)
tmp.close()
return tmp.name
# --- Gradio UI ---
with gr.Blocks(title="RoofSight", theme=gr.themes.Soft()) as demo:
gr.Markdown("# RoofSight")
gr.Markdown(
"Roof plane segmentation from satellite imagery. \n"
"**C-RADIOv4-H** (DINOv3 + SAM3 + SigLIP2) + **RANSAC** 3D plane fitting."
)
with gr.Row():
# --- Left column: inputs ---
with gr.Column(scale=1):
address = gr.Textbox(
label="Property Address",
placeholder="123 Main St, City, ST 12345",
)
api_key = gr.Textbox(
label="Google API Key",
type="password",
placeholder="Leave blank to use server key",
)
with gr.Accordion("Settings", open=False):
radius = gr.Slider(
25, 100, value=50, step=5,
label="Search Radius (m)",
)
ransac_thresh = gr.Slider(
0.05, 0.50, value=0.15, step=0.01,
label="RANSAC Threshold (m)",
info="Distance tolerance for plane inliers. Lower = tighter fit.",
)
max_planes = gr.Slider(
2, 15, value=8, step=1,
label="Max Planes",
)
min_area = gr.Slider(
10, 500, value=50, step=10,
label="Min Area (sqft)",
info="Polygons smaller than this are excluded.",
)
run_btn = gr.Button("Segment Roof", variant="primary", size="lg")
# --- Right column: outputs ---
with gr.Column(scale=2):
with gr.Row():
orig_img = gr.Image(label="Satellite Image", show_download_button=False)
overlay_img = gr.Image(label="Roof Planes", show_download_button=True)
status_md = gr.Markdown(label="Status")
with gr.Accordion("GeoJSON Output", open=True):
geojson_out = gr.Code(language="json", lines=15, label="GeoJSON")
with gr.Row():
dl_btn = gr.Button("Download GeoJSON", size="sm")
dl_file = gr.File(label="Download", visible=True)
# --- Wiring ---
run_btn.click(
fn=process,
inputs=[address, api_key, radius, ransac_thresh, max_planes, min_area],
outputs=[orig_img, overlay_img, geojson_out, status_md],
)
dl_btn.click(fn=save_geojson, inputs=[geojson_out], outputs=[dl_file])
if __name__ == "__main__":
demo.launch()
|