File size: 5,920 Bytes
64e387f
 
 
 
f1c6a8d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64e387f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d22607c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64e387f
 
 
 
 
d22607c
 
 
 
 
 
 
 
 
 
 
 
 
 
64e387f
 
 
 
 
 
 
 
 
 
 
 
 
 
d22607c
 
 
 
64e387f
 
d22607c
 
 
 
 
 
 
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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)