ChristianQ commited on
Commit
9c67a05
·
1 Parent(s): 0a004be

Updated app.py and detection module for more testing logs

Browse files
Files changed (2) hide show
  1. app.py +111 -143
  2. visualization.py +19 -1
app.py CHANGED
@@ -5,32 +5,38 @@ import traceback
5
 
6
  import gradio as gr
7
  from fastapi import FastAPI, File, UploadFile
8
- from fastapi.responses import JSONResponse
9
  from fastapi.middleware.cors import CORSMiddleware
10
  from fastapi.responses import JSONResponse, FileResponse
11
 
12
  from visualization import process_wireframe
13
  from CodingModule import HTMLGenerator
14
 
15
- # -----------------------------------------------------------------------------
16
- # FASTAPI (for Firebase / programmatic access)
17
- # -----------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  api = FastAPI()
19
 
20
  api.add_middleware(
21
  CORSMiddleware,
22
- allow_origins=["*"], #Replace with the Firebase domain
23
  allow_credentials=True,
24
  allow_methods=["*"],
25
  allow_headers=["*"],
26
  )
27
 
28
- TEMP_DIR = "./temp"
29
- OUTPUT_DIR = "./output"
30
-
31
- os.makedirs(TEMP_DIR, exist_ok=True)
32
- os.makedirs(OUTPUT_DIR, exist_ok=True)
33
-
34
 
35
  @api.get("/")
36
  def health_check():
@@ -43,9 +49,24 @@ async def process_wireframe_api(image: UploadFile = File(...)):
43
  temp_path = os.path.join(TEMP_DIR, f"{file_id}_{image.filename}")
44
 
45
  try:
 
 
 
46
  with open(temp_path, "wb") as f:
47
  shutil.copyfileobj(image.file, f)
48
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  results = process_wireframe(
50
  image_path=temp_path,
51
  save_json=True,
@@ -53,28 +74,33 @@ async def process_wireframe_api(image: UploadFile = File(...)):
53
  show_visualization=False
54
  )
55
 
56
- if not results or not results.get('normalized_elements'):
57
  return JSONResponse(
58
  status_code=400,
59
  content={"error": "No elements detected"}
60
  )
61
-
62
  json_path = results.get("json_path")
63
-
64
- #Generate styled HTML using CodingModule
 
 
65
  try:
66
- html_generator = HTMLGenerator(json_path)
67
  html_filename = f"{file_id}_styled.html"
68
- html_path = os.path.join(OUTPUT_DIR. html_filename)
69
- html_generator.generate_html(html_path)
 
 
 
70
  except Exception as e:
71
- print(f"Warning: Could not generate styled HTML: {e}")
72
  html_path = results.get("html_path")
73
-
74
  return {
75
  "success": True,
76
- "json_path": results.get("./", ""),
77
- "html_path": results.get("./", ""),
 
78
  "total_elements": len(results["normalized_elements"])
79
  }
80
 
@@ -89,49 +115,40 @@ async def process_wireframe_api(image: UploadFile = File(...)):
89
  if os.path.exists(temp_path):
90
  os.remove(temp_path)
91
 
92
- #Endpoint to serve generated HTML files
93
- @api.get("/view-html/{file_id}")
94
- async def serve_output_file(filename: str):
95
- file_path = os.path.join(OUTPUT_DIR, filename)
96
- if os.path.exists(file_path):
97
- if filename.endswith('.html'):
98
- return FileResponse(file_path, media_type="text/html")
99
- elif filename.endswith('.json'):
100
- return FileResponse(file_path, media_type="application/json")
101
- else:
102
- return FileResponse(file_path)
103
- return JSONResponse(status_code=404, content={"error": "File not Found"})
104
-
105
- # Legacy endpoint for just for backward compatibility
106
- @api.get("/view-html/{file_id}")
107
- async def view_html(file_id: str):
108
- html_path = os.path.join(OUTPUT_DIR, f"{file_id}.html")
109
- if os.path.exists(html_path):
110
- return FileResponse(html_path, media_type="text/html")
111
- # Try with _styled suffix
112
- html_path = os.path.join(OUTPUT_DIR, f"{file_id}_styled.html")
113
- if os.path.exists(html_path):
114
- return FileResponse(html_path, media_type="text/html")
115
- return JSONResponse(status_code=404, content={"error": "File not found"})
116
-
117
- # -----------------------------------------------------------------------------
118
- # GRADIO (for Hugging Face UI)
119
- # -----------------------------------------------------------------------------
120
  def gradio_process(image):
121
- """
122
- Gradio passes a PIL Image.
123
- We save it temporarily and reuse the SAME pipeline.
124
- """
125
  if image is None:
126
- return "Please upload an image", None, None
127
-
128
  file_id = str(uuid.uuid4())
129
  temp_path = os.path.join(TEMP_DIR, f"{file_id}.png")
130
-
131
- try:
132
- image.save(temp_path)
133
- except Exception as e:
134
- return f"Error saving image: {str(e)}", None, None
135
 
136
  try:
137
  results = process_wireframe(
@@ -141,103 +158,54 @@ def gradio_process(image):
141
  show_visualization=False
142
  )
143
 
144
- if not results or not results.get('normalized_elements'):
145
  return "No elements detected", None, None
146
 
147
  json_path = results.get("json_path")
148
-
149
- #Generate styled HTML using CodingModule
150
  try:
151
- html_generator = HTMLGenerator(json_path)
152
  html_filename = f"{file_id}_styled.html"
153
  html_path = os.path.join(OUTPUT_DIR, html_filename)
154
- html_generator.generate_html(html_path)
155
- except Exception as e:
156
- print(f"Warning: Could not generate styled HTML: {e}")
157
- traceback.print_exc()
 
158
  html_path = results.get("html_path")
159
-
160
- num_elements = len(results[['normalized_elements']])
161
- status_msg = f"Successfully detected {num_elements} elemnts\n"
162
- status_msg += f"JSON: {os.path.basename(json_path)}\n"
163
- status_msg += f"HTML: {os.path.basename(html_path)}"
164
-
165
- return status_msg, json_path, html_path
166
 
167
  except Exception as e:
168
  traceback.print_exc()
169
- return f"Error: {str(e)}", None, None
170
 
171
  finally:
172
- if os.path.exists(temp_path):
173
- os.remove(temp_path)
174
 
175
- # Enhanced Gradio interface with better UI
176
- with gr.Blocks(theme=gr.themes.Soft(), title="Wireframe Layout Normalizer") as demo:
177
  gr.Markdown("# Wireframe Layout Normalizer")
178
- gr.Markdown("Upload a wireframe image to extract and normalize UI layout elements")
179
-
180
- gr.Markdown("""
181
- ### Academic Research Project made by SeroTech 2025-2026 Holy Angel University
182
- This tool is part of a thesis project. Upload your wireframe sketch to automatically:
183
- - Detect UI elements (buttons, text fields, images, etc.)
184
- - Normalize positions and alignments
185
- - Export structured JSON data
186
- - Generate beautiful HTML preview
187
- """)
188
-
189
- with gr.Row():
190
- with gr.Column(scale=1):
191
- image_input = gr.Image(type="pil", label="Upload Wireframe Image")
192
- process_btn = gr.Button("Process Wireframe", variant="primary", size="lg")
193
-
194
- gr.Markdown("""
195
- #### Tips:
196
- - Upload clear wireframe sketches
197
- - PNG, JPG, or JPEG formats supported
198
- - Higher resolution = better detection
199
- """)
200
-
201
- with gr.Column(scale=1):
202
- status_output = gr.Textbox(
203
- label="Processing Status",
204
- lines=3,
205
- placeholder="Upload an image and click Process to begin..."
206
- )
207
- json_output = gr.File(label="Normalized JSON Output")
208
- html_output = gr.File(label="Generated HTML Preview")
209
-
210
- process_btn.click(
211
- fn=gradio_process,
212
- inputs=image_input,
213
- outputs=[status_output, json_output, html_output]
214
- )
215
-
216
- gr.Markdown("""
217
- ---
218
- ### How to use:
219
- 1. **Upload** a wireframe image (hand-drawn or digital)
220
- 2. **Click** "Process Wireframe" and wait for processing
221
- 3. **Download** the JSON file for structured data
222
- 4. **Download** the HTML file to see a styled preview
223
-
224
- ### Output Files:
225
- - **JSON**: Contains all detected elements with positions, types, and grid data
226
- - **HTML**: Beautiful, responsive preview with modern styling
227
-
228
- ### Technical Details:
229
- - Model: TensorFlow-based object detection
230
- - Grid System: 24-column responsive layout
231
- - Supported Elements: Buttons, Checkboxes, Text Fields, Images, Paragraphs, Text, Navbars
232
- """)
233
-
234
-
235
- # -----------------------------------------------------------------------------
236
- # ENTRY POINT (THIS IS IMPORTANT)
237
- # -----------------------------------------------------------------------------
238
  app = gr.mount_gradio_app(api, demo, path="/")
239
 
 
240
  if __name__ == "__main__":
241
  import uvicorn
242
- # For local testing
243
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
5
 
6
  import gradio as gr
7
  from fastapi import FastAPI, File, UploadFile
 
8
  from fastapi.middleware.cors import CORSMiddleware
9
  from fastapi.responses import JSONResponse, FileResponse
10
 
11
  from visualization import process_wireframe
12
  from CodingModule import HTMLGenerator
13
 
14
+
15
+ # ============================================================
16
+ # DIRECTORIES
17
+ # ============================================================
18
+
19
+ TEMP_DIR = "./temp"
20
+ OUTPUT_DIR = "./output"
21
+
22
+ os.makedirs(TEMP_DIR, exist_ok=True)
23
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
24
+
25
+
26
+ # ============================================================
27
+ # FASTAPI BACKEND
28
+ # ============================================================
29
+
30
  api = FastAPI()
31
 
32
  api.add_middleware(
33
  CORSMiddleware,
34
+ allow_origins=["*"], # change to your Firebase domain later
35
  allow_credentials=True,
36
  allow_methods=["*"],
37
  allow_headers=["*"],
38
  )
39
 
 
 
 
 
 
 
40
 
41
  @api.get("/")
42
  def health_check():
 
49
  temp_path = os.path.join(TEMP_DIR, f"{file_id}_{image.filename}")
50
 
51
  try:
52
+ # ----------------------------------------------------
53
+ # SAVE UPLOADED IMAGE
54
+ # ----------------------------------------------------
55
  with open(temp_path, "wb") as f:
56
  shutil.copyfileobj(image.file, f)
57
 
58
+ file_size = os.path.getsize(temp_path)
59
+ print("Uploaded file size:", file_size)
60
+
61
+ if file_size == 0:
62
+ return JSONResponse(
63
+ status_code=400,
64
+ content={"error": "Uploaded file is empty — check frontend upload format."}
65
+ )
66
+
67
+ # ----------------------------------------------------
68
+ # RUN DETECTION PIPELINE
69
+ # ----------------------------------------------------
70
  results = process_wireframe(
71
  image_path=temp_path,
72
  save_json=True,
 
74
  show_visualization=False
75
  )
76
 
77
+ if not results or not results.get("normalized_elements"):
78
  return JSONResponse(
79
  status_code=400,
80
  content={"error": "No elements detected"}
81
  )
82
+
83
  json_path = results.get("json_path")
84
+
85
+ # ----------------------------------------------------
86
+ # GENERATE STYLED HTML
87
+ # ----------------------------------------------------
88
  try:
 
89
  html_filename = f"{file_id}_styled.html"
90
+ html_path = os.path.join(OUTPUT_DIR, html_filename)
91
+
92
+ generator = HTMLGenerator(json_path)
93
+ generator.generate_html(html_path)
94
+
95
  except Exception as e:
96
+ print("HTML generation error:", e)
97
  html_path = results.get("html_path")
98
+
99
  return {
100
  "success": True,
101
+ "file_id": file_id,
102
+ "json_path": json_path,
103
+ "html_path": html_path,
104
  "total_elements": len(results["normalized_elements"])
105
  }
106
 
 
115
  if os.path.exists(temp_path):
116
  os.remove(temp_path)
117
 
118
+
119
+ # ============================================================
120
+ # SERVE GENERATED FILES (HTML / JSON)
121
+ # ============================================================
122
+
123
+ @api.get("/output/{filename}")
124
+ async def serve_output(filename: str):
125
+ path = os.path.join(OUTPUT_DIR, filename)
126
+ if not os.path.exists(path):
127
+ return JSONResponse(status_code=404,
128
+ content={"error": "File not found"})
129
+
130
+ if filename.endswith(".html"):
131
+ return FileResponse(path, media_type="text/html")
132
+
133
+ if filename.endswith(".json"):
134
+ return FileResponse(path, media_type="application/json")
135
+
136
+ return FileResponse(path)
137
+
138
+
139
+ # ============================================================
140
+ # GRADIO FRONTEND (for HF testing only)
141
+ # ============================================================
142
+
 
 
 
143
  def gradio_process(image):
144
+
 
 
 
145
  if image is None:
146
+ return "Upload an image first", None, None
147
+
148
  file_id = str(uuid.uuid4())
149
  temp_path = os.path.join(TEMP_DIR, f"{file_id}.png")
150
+
151
+ image.save(temp_path)
 
 
 
152
 
153
  try:
154
  results = process_wireframe(
 
158
  show_visualization=False
159
  )
160
 
161
+ if not results or not results.get("normalized_elements"):
162
  return "No elements detected", None, None
163
 
164
  json_path = results.get("json_path")
165
+
 
166
  try:
 
167
  html_filename = f"{file_id}_styled.html"
168
  html_path = os.path.join(OUTPUT_DIR, html_filename)
169
+
170
+ generator = HTMLGenerator(json_path)
171
+ generator.generate_html(html_path)
172
+
173
+ except Exception:
174
  html_path = results.get("html_path")
175
+
176
+ return (
177
+ f"Detected {len(results['normalized_elements'])} elements",
178
+ json_path,
179
+ html_path
180
+ )
 
181
 
182
  except Exception as e:
183
  traceback.print_exc()
184
+ return f"Error: {e}", None, None
185
 
186
  finally:
187
+ os.remove(temp_path)
188
+
189
 
190
+ with gr.Blocks(title="Wireframe Layout Normalizer") as demo:
 
191
  gr.Markdown("# Wireframe Layout Normalizer")
192
+
193
+ inp = gr.Image(type="pil")
194
+ btn = gr.Button("Process")
195
+ out1 = gr.Textbox(label="Status")
196
+ out2 = gr.File(label="JSON")
197
+ out3 = gr.File(label="HTML")
198
+
199
+ btn.click(gradio_process, inp, [out1, out2, out3])
200
+
201
+
202
+ # ============================================================
203
+ # MOUNT FASTAPI + GRADIO
204
+ # ============================================================
205
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  app = gr.mount_gradio_app(api, demo, path="/")
207
 
208
+
209
  if __name__ == "__main__":
210
  import uvicorn
211
+ uvicorn.run(app, host="0.0.0.0", port=7860)
 
visualization.py CHANGED
@@ -1227,14 +1227,24 @@ def process_wireframe(image_path: str,
1227
  Dictionary containing all processing results
1228
  """
1229
 
 
 
 
 
 
1230
  print("=" * 80)
1231
  print("🚀 WIREFRAME LAYOUT NORMALIZER")
1232
  print("=" * 80)
1233
 
1234
  # Step 1: Load model and get predictions
1235
  global model
 
 
1236
  if model is None:
1237
  print("\n📦 Loading model...")
 
 
 
1238
  try:
1239
  model = tf.keras.models.load_model(
1240
  MODEL_PATH,
@@ -1252,11 +1262,19 @@ def process_wireframe(image_path: str,
1252
  return {}
1253
 
1254
  print(f"\n📸 Processing image: {image_path}")
 
 
 
 
1255
  pil_img, elements = get_predictions(image_path)
1256
  print(f"✅ Detected {len(elements)} elements")
1257
 
1258
  if not elements:
1259
- print("⚠️ No elements detected. Exiting.")
 
 
 
 
1260
  return {}
1261
 
1262
  # Step 2: Normalize layout
 
1227
  Dictionary containing all processing results
1228
  """
1229
 
1230
+ print("=== PROCESS_WIREFRAME START ===")
1231
+ print("Input image path:", image_path)
1232
+ print("File exists:", os.path.exists(image_path))
1233
+ print("File size:", os.path.getsize(image_path))
1234
+
1235
  print("=" * 80)
1236
  print("🚀 WIREFRAME LAYOUT NORMALIZER")
1237
  print("=" * 80)
1238
 
1239
  # Step 1: Load model and get predictions
1240
  global model
1241
+ print("Model object is None?", model is None)
1242
+ print("Model path exists?", os.path.exists(MODEL_PATH))
1243
  if model is None:
1244
  print("\n📦 Loading model...")
1245
+ print("Attempting to load keras model:", MODEL_PATH)
1246
+ print("Loaded model summary:")
1247
+ model.summary(print_fn=lambda x: print(x))
1248
  try:
1249
  model = tf.keras.models.load_model(
1250
  MODEL_PATH,
 
1262
  return {}
1263
 
1264
  print(f"\n📸 Processing image: {image_path}")
1265
+ print("Running detection inference…")
1266
+ print("Elements detected:", len(elements))
1267
+ for elem in elements:
1268
+ print(" -", elem.label, elem.score, elem.bbox)
1269
  pil_img, elements = get_predictions(image_path)
1270
  print(f"✅ Detected {len(elements)} elements")
1271
 
1272
  if not elements:
1273
+ print("⚠️ No detection output returned.")
1274
+ print("→ Meaning model.predict returned zero raw boxes.")
1275
+ print("→ Check thresholds:")
1276
+ print("CONF_THRESHOLD:", CONF_THRESHOLD)
1277
+ print("IOU_THRESHOLD:", IOU_THRESHOLD)
1278
  return {}
1279
 
1280
  # Step 2: Normalize layout