import gradio as gr from PIL import Image, ImageDraw import tempfile import json import os import requests from gradio_client import utils as gradio_client_utils _original_json_schema_to_python_type = gradio_client_utils._json_schema_to_python_type def _safe_json_schema_to_python_type(schema, defs=None): """Compatibility patch for Gradio 4.44 / gradio_client 1.3 schema parsing. Some component schemas contain additionalProperties as a boolean. Older gradio_client assumes that value is always a dict and crashes while building API info during Space startup. """ if isinstance(schema, bool): return "Any" if isinstance(schema, dict) and isinstance(schema.get("additionalProperties"), bool): schema = dict(schema) schema["additionalProperties"] = {} return _original_json_schema_to_python_type(schema, defs) gradio_client_utils._json_schema_to_python_type = _safe_json_schema_to_python_type # Roboflow API configuration ROBOFLOW_API_KEY = os.getenv("ROBOFLOW_API_KEY") ROOM_MODEL = "room-segmentation-frntt/1" DOOR_WINDOW_MODEL = "door-detection-model/2" color_options = ["Red", "Green", "Blue", "Yellow"] layer_options = ["Room Detection", "Doors and Windows Detection"] def roboflow_infer(image_path, model_id, api_key): """Direct HTTP call to Roboflow inference API""" if not api_key: return {"predictions": [], "error": "Missing ROBOFLOW_API_KEY Space secret"} url = f"https://detect.roboflow.com/{model_id}" params = {"api_key": api_key} with open(image_path, "rb") as f: files = {"file": f} response = requests.post(url, params=params, files=files) if response.status_code == 200: return response.json() else: print(f"API Error: {response.status_code} - {response.text}") return {"predictions": []} def apply_zoom(image, zoom_factor): width, height = image.size new_width = int(width * zoom_factor) new_height = int(height * zoom_factor) return image.resize((new_width, new_height)) def detect_and_draw(image_path, model_id, filter_classes=None, color_choice=None): result = roboflow_infer(image_path, model_id, ROBOFLOW_API_KEY) original_img = Image.open(image_path) overlayed_img = original_img.copy() draw = ImageDraw.Draw(overlayed_img) counts = {} for prediction in result.get('predictions', []): pred_class = prediction.get('class', '').lower() if filter_classes and pred_class not in filter_classes: continue counts[pred_class] = counts.get(pred_class, 0) + 1 x = int(prediction['x'] - prediction['width'] / 2) y = int(prediction['y'] - prediction['height'] / 2) width = int(prediction['width']) height = int(prediction['height']) draw.rectangle([x, y, x + width, y + height], outline=color_choice, width=2) label = f"{pred_class}" draw.text((x, y - 10), label, fill=color_choice) return overlayed_img, counts def process_floor_plan(image, zoom_factor, color_choice, selected_layers): try: if image is None: return None, None, json.dumps({"error": "Please upload a floor plan image."}, indent=2) selected_layers = selected_layers or [] with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp_file: image.save(temp_file.name) temp_file_path = temp_file.name zoomed_image = apply_zoom(Image.open(temp_file_path), zoom_factor) zoomed_image.save(temp_file_path) room_overlay = None dw_overlay = None if "Room Detection" in selected_layers: room_overlay, room_counts = detect_and_draw( temp_file_path, ROOM_MODEL, filter_classes=["room"], color_choice=color_choice ) else: room_counts = {} if "Doors and Windows Detection" in selected_layers: dw_overlay, dw_counts = detect_and_draw( temp_file_path, DOOR_WINDOW_MODEL, filter_classes=["door", "window"], color_choice=color_choice ) else: dw_counts = {} combined_counts = {} combined_counts.update(room_counts) combined_counts.update(dw_counts) return room_overlay, dw_overlay, json.dumps(combined_counts, indent=2) except Exception as e: return None, None, json.dumps({"error": str(e)}, indent=2) # Gradio 5.x Interface with gr.Blocks(title="Floor Plan Detection") as demo: gr.Markdown("# 🏗️ Floor Plan Detection") gr.Markdown("Upload a floor plan image to detect rooms, doors, and windows.") with gr.Row(): with gr.Column(): input_image = gr.Image(type="pil", label="Upload Floor Plan") zoom_factor = gr.Slider(minimum=0.5, maximum=3.0, value=1.0, step=0.1, label="Zoom Factor") color_choice = gr.Dropdown(choices=color_options, value="Red", label="Annotation Color") selected_layers = gr.CheckboxGroup(choices=layer_options, value=layer_options, label="Detection Layers") process_btn = gr.Button("Process", variant="primary") with gr.Column(): room_output = gr.Image(type="pil", label="Room Detection") door_window_output = gr.Image(type="pil", label="Doors and Windows Detection") output_json = gr.Textbox(label="Detection Results (JSON)", lines=10) process_btn.click( fn=process_floor_plan, inputs=[input_image, zoom_factor, color_choice, selected_layers], outputs=[room_output, door_window_output, output_json] ) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860, show_api=False)