Manvikk commited on
Commit
c0d4e9d
Β·
verified Β·
1 Parent(s): 5e362f0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +332 -150
app.py CHANGED
@@ -2,7 +2,7 @@ import os
2
  import cv2
3
  import numpy as np
4
  import base64
5
- from flask import Flask, render_template_string, request, redirect, flash
6
  import roboflow
7
  import torch
8
  from collections import Counter
@@ -33,8 +33,6 @@ except Exception as e:
33
  # --- YOLOv5 Pretrained Model for Persons & Cars ---
34
  try:
35
  yolov5_model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True)
36
- # Filter YOLO detections to only include persons and cars.
37
- YOLO_FILTER_CLASSES = {"person", "car"}
38
  print("YOLOv5 model loaded successfully.")
39
  except Exception as e:
40
  print("Error loading YOLOv5 model:", e)
@@ -71,7 +69,7 @@ def custom_nms(preds, iou_threshold=0.3):
71
  filtered_preds.append(pred)
72
  return filtered_preds
73
 
74
- def process_image(image_path):
75
  # Load image
76
  image = cv2.imread(image_path)
77
  if image is None:
@@ -79,124 +77,131 @@ def process_image(image_path):
79
  return None, "Could not read the image."
80
 
81
  img_height, img_width = image.shape[:2]
82
- detection_info = [] # List to hold all detection results for display
83
 
84
- # --- (a) Roboflow Box Detection & Measurement ---
85
- if box_model is None:
86
- print("DEBUG: Roboflow model is not initialized.")
87
- return None, "Roboflow model is not available."
88
- try:
89
- results = box_model.predict(image_path, confidence=50, overlap=30).json()
90
- predictions = results.get("predictions", [])
91
- except Exception as e:
92
- print("DEBUG: Error during Roboflow prediction:", e)
93
- return None, "Error during Roboflow prediction."
94
-
95
- processed_preds = []
96
- for prediction in predictions:
97
  try:
98
- x, y, width, height = prediction["x"], prediction["y"], prediction["width"], prediction["height"]
99
- x1 = int(round(x - width / 2))
100
- y1 = int(round(y - height / 2))
101
- x2 = int(round(x + width / 2))
102
- y2 = int(round(y + height / 2))
103
- # Clamp coordinates to image dimensions
104
- x1 = max(0, min(x1, img_width - 1))
105
- y1 = max(0, min(y1, img_height - 1))
106
- x2 = max(0, min(x2, img_width - 1))
107
- y2 = max(0, min(y2, img_height - 1))
108
- processed_preds.append({
109
- "box": (x1, y1, x2, y2),
110
- "class": prediction["class"],
111
- "confidence": prediction["confidence"]
112
- })
113
  except Exception as e:
114
- print("DEBUG: Error processing a prediction:", e)
115
- continue
116
 
117
- box_detections = custom_nms(processed_preds, iou_threshold=0.3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
- # Detect ArUco marker for measurement (only applicable for boxes)
120
- marker_real_width_cm = 10.0 # The marker is 10cm x 10cm
121
- try:
122
- gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
123
- aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_6X6_250)
124
- aruco_params = cv2.aruco.DetectorParameters()
125
- corners, ids, _ = cv2.aruco.detectMarkers(gray, aruco_dict, parameters=aruco_params)
126
- if ids is not None and len(corners) > 0:
127
- marker_corners = corners[0].reshape((4, 2))
128
- cv2.aruco.drawDetectedMarkers(image, corners, ids)
129
- marker_width_pixels = np.linalg.norm(marker_corners[0] - marker_corners[1])
130
- marker_height_pixels = np.linalg.norm(marker_corners[1] - marker_corners[2])
131
- marker_pixel_size = (marker_width_pixels + marker_height_pixels) / 2.0
132
- conversion_factor = marker_real_width_cm / marker_pixel_size
133
- else:
 
 
 
 
 
134
  conversion_factor = None
135
- except Exception as e:
136
- print("DEBUG: Error during ArUco detection:", e)
137
- conversion_factor = None
138
 
139
- # Draw box detections and record measurement info (only for boxes)
140
- for pred in box_detections:
141
- x1, y1, x2, y2 = pred["box"]
142
- label = pred["class"]
143
- confidence = pred["confidence"]
144
- cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
145
- if conversion_factor is not None:
146
- box_width_pixels = x2 - x1
147
- box_height_pixels = y2 - y1
148
- box_width_cm = box_width_pixels * conversion_factor
149
- box_height_cm = box_height_pixels * conversion_factor
150
- size_text = f"{box_width_cm:.1f}x{box_height_cm:.1f} cm"
151
- detection_info.append({
152
- "class": label,
153
- "confidence": f"{confidence:.2f}",
154
- "width_cm": f"{box_width_cm:.1f}",
155
- "height_cm": f"{box_height_cm:.1f}"
156
- })
157
- else:
158
- size_text = ""
159
- detection_info.append({
160
- "class": label,
161
- "confidence": f"{confidence:.2f}",
162
- "width_cm": "N/A",
163
- "height_cm": "N/A"
164
- })
165
- text = f"{label} ({confidence:.2f}) {size_text}"
166
- (text_width, text_height), baseline = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
167
- cv2.rectangle(image, (x1, y1 - text_height - baseline - 5), (x1 + text_width, y1 - 5), (0, 255, 0), -1)
168
- cv2.putText(image, text, (x1, y1 - 5 - baseline), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
169
 
170
- # --- (b) YOLOv5 for Persons & Cars ---
171
- if yolov5_model is None:
172
- print("DEBUG: YOLOv5 model is not initialized.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  else:
174
- try:
175
- img_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
176
- yolo_results = yolov5_model(img_rgb)
177
- df = yolo_results.pandas().xyxy[0]
178
- for _, row in df.iterrows():
179
- if row['name'] in YOLO_FILTER_CLASSES:
180
- xmin = int(row['xmin'])
181
- ymin = int(row['ymin'])
182
- xmax = int(row['xmax'])
183
- ymax = int(row['ymax'])
184
- conf = row['confidence']
185
- label = row['name']
186
- cv2.rectangle(image, (xmin, ymin), (xmax, ymax), (255, 0, 0), 2)
187
- text = f"{label} ({conf:.2f})"
188
- (text_width, text_height), baseline = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
189
- cv2.rectangle(image, (xmin, ymin - text_height - baseline - 5), (xmin + text_width, ymin - 5), (255, 0, 0), -1)
190
- cv2.putText(image, text, (xmin, ymin - 5 - baseline), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
191
- detection_info.append({
192
- "class": label,
193
- "confidence": f"{conf:.2f}",
194
- "width_cm": "N/A",
195
- "height_cm": "N/A"
196
- })
197
- except Exception as e:
198
- print("DEBUG: Error during YOLOv5 inference:", e)
199
- return None, "Error during YOLOv5 inference."
200
 
201
  # --- Build Top Summary Text ---
202
  detection_counts = Counter(det["class"] for det in detection_info)
@@ -209,40 +214,73 @@ def process_image(image_path):
209
  return image, detection_info
210
 
211
  #########################################
212
- # 3. Flask Routes
213
  #########################################
214
 
215
- @app.route('/', methods=['GET', 'POST'])
216
- def index():
217
- image_data = None
218
- detection_info = None
219
- if request.method == 'POST':
220
- if 'file' not in request.files:
221
- flash('No file part')
222
- return redirect(request.url)
223
- file = request.files['file']
224
- if file.filename == '':
225
- flash('No selected file')
226
- return redirect(request.url)
227
- upload_path = "uploaded.jpg"
228
- try:
229
- file.save(upload_path)
230
- except Exception as e:
231
- print("DEBUG: Error saving uploaded file:", e)
232
- flash("Error saving uploaded file.")
233
- return redirect(request.url)
234
- processed_image, detection_info = process_image(upload_path)
235
- if processed_image is None:
236
- flash("Error Processing Image: " + detection_info)
237
- else:
238
- retval, buffer = cv2.imencode('.jpg', processed_image)
239
- image_data = base64.b64encode(buffer).decode('utf-8')
240
- try:
241
- os.remove(upload_path)
242
- except Exception as e:
243
- print("DEBUG: Error removing uploaded file:", e)
244
- return render_template_string('''
245
- <!DOCTYPE html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  <html lang="en">
247
  <head>
248
  <meta charset="UTF-8">
@@ -291,6 +329,54 @@ def index():
291
  color: #000;
292
  font-family: "Share Tech Mono", monospace;
293
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  .content-wrapper {
295
  display: flex;
296
  flex-direction: row;
@@ -339,9 +425,21 @@ def index():
339
  </style>
340
  </head>
341
  <body>
 
 
 
 
 
 
 
 
 
342
  <div class="typing-effect" id="typing"></div>
343
- <form method="post" enctype="multipart/form-data">
 
344
  <input type="file" name="file" accept="image/*" required>
 
 
345
  <button type="submit">Analyze Image</button>
346
  </form>
347
  {% if image_data or detection_info %}
@@ -373,6 +471,40 @@ def index():
373
  {% endif %}
374
  <div class="footer">&copy; 2024 MathLens AI Detection App. All rights reserved.</div>
375
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  const textArray = ["MathLens", "Smart Counting with Maths"];
377
  let textIndex = 0;
378
  let charIndex = 0;
@@ -401,15 +533,65 @@ def index():
401
  </script>
402
  </body>
403
  </html>
 
404
 
 
 
 
405
 
406
- ''', image_data=image_data, detection_info=detection_info)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
 
408
  #########################################
409
- # Run the App
410
  #########################################
411
 
412
  if __name__ == '__main__':
413
- # Ensure the app runs on 0.0.0.0 and port 7860 for Hugging Face Spaces.
414
  app.run(host="0.0.0.0", port=7860)
415
-
 
2
  import cv2
3
  import numpy as np
4
  import base64
5
+ from flask import Flask, render_template_string, request, redirect, flash, url_for
6
  import roboflow
7
  import torch
8
  from collections import Counter
 
33
  # --- YOLOv5 Pretrained Model for Persons & Cars ---
34
  try:
35
  yolov5_model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True)
 
 
36
  print("YOLOv5 model loaded successfully.")
37
  except Exception as e:
38
  print("Error loading YOLOv5 model:", e)
 
69
  filtered_preds.append(pred)
70
  return filtered_preds
71
 
72
+ def process_image(image_path, object_type):
73
  # Load image
74
  image = cv2.imread(image_path)
75
  if image is None:
 
77
  return None, "Could not read the image."
78
 
79
  img_height, img_width = image.shape[:2]
80
+ detection_info = [] # List to hold detection results for display
81
 
82
+ if object_type == "box":
83
+ # --- Roboflow Box Detection & Measurement ---
84
+ if box_model is None:
85
+ print("DEBUG: Roboflow model is not initialized.")
86
+ return None, "Roboflow model is not available."
 
 
 
 
 
 
 
 
87
  try:
88
+ results = box_model.predict(image_path, confidence=50, overlap=30).json()
89
+ predictions = results.get("predictions", [])
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  except Exception as e:
91
+ print("DEBUG: Error during Roboflow prediction:", e)
92
+ return None, "Error during Roboflow prediction."
93
 
94
+ processed_preds = []
95
+ for prediction in predictions:
96
+ try:
97
+ x, y, width, height = prediction["x"], prediction["y"], prediction["width"], prediction["height"]
98
+ x1 = int(round(x - width / 2))
99
+ y1 = int(round(y - height / 2))
100
+ x2 = int(round(x + width / 2))
101
+ y2 = int(round(y + height / 2))
102
+ # Clamp coordinates to image dimensions
103
+ x1 = max(0, min(x1, img_width - 1))
104
+ y1 = max(0, min(y1, img_height - 1))
105
+ x2 = max(0, min(x2, img_width - 1))
106
+ y2 = max(0, min(y2, img_height - 1))
107
+ processed_preds.append({
108
+ "box": (x1, y1, x2, y2),
109
+ "class": prediction["class"],
110
+ "confidence": prediction["confidence"]
111
+ })
112
+ except Exception as e:
113
+ print("DEBUG: Error processing a prediction:", e)
114
+ continue
115
 
116
+ box_detections = custom_nms(processed_preds, iou_threshold=0.3)
117
+
118
+ # Detect ArUco marker for measurement (only applicable for boxes)
119
+ marker_real_width_cm = 10.0 # The marker is 10cm x 10cm
120
+ try:
121
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
122
+ aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_6X6_250)
123
+ aruco_params = cv2.aruco.DetectorParameters()
124
+ corners, ids, _ = cv2.aruco.detectMarkers(gray, aruco_dict, parameters=aruco_params)
125
+ if ids is not None and len(corners) > 0:
126
+ marker_corners = corners[0].reshape((4, 2))
127
+ cv2.aruco.drawDetectedMarkers(image, corners, ids)
128
+ marker_width_pixels = np.linalg.norm(marker_corners[0] - marker_corners[1])
129
+ marker_height_pixels = np.linalg.norm(marker_corners[1] - marker_corners[2])
130
+ marker_pixel_size = (marker_width_pixels + marker_height_pixels) / 2.0
131
+ conversion_factor = marker_real_width_cm / marker_pixel_size
132
+ else:
133
+ conversion_factor = None
134
+ except Exception as e:
135
+ print("DEBUG: Error during ArUco detection:", e)
136
  conversion_factor = None
 
 
 
137
 
138
+ # Draw box detections and record measurement info
139
+ for pred in box_detections:
140
+ x1, y1, x2, y2 = pred["box"]
141
+ label = pred["class"]
142
+ confidence = pred["confidence"]
143
+ cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
144
+ if conversion_factor is not None:
145
+ box_width_pixels = x2 - x1
146
+ box_height_pixels = y2 - y1
147
+ box_width_cm = box_width_pixels * conversion_factor
148
+ box_height_cm = box_height_pixels * conversion_factor
149
+ size_text = f"{box_width_cm:.1f}x{box_height_cm:.1f} cm"
150
+ detection_info.append({
151
+ "class": label,
152
+ "confidence": f"{confidence:.2f}",
153
+ "width_cm": f"{box_width_cm:.1f}",
154
+ "height_cm": f"{box_height_cm:.1f}"
155
+ })
156
+ else:
157
+ size_text = ""
158
+ detection_info.append({
159
+ "class": label,
160
+ "confidence": f"{confidence:.2f}",
161
+ "width_cm": "N/A",
162
+ "height_cm": "N/A"
163
+ })
164
+ text = f"{label} ({confidence:.2f}) {size_text}"
165
+ (text_width, text_height), baseline = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
166
+ cv2.rectangle(image, (x1, y1 - text_height - baseline - 5), (x1 + text_width, y1 - 5), (0, 255, 0), -1)
167
+ cv2.putText(image, text, (x1, y1 - 5 - baseline), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
168
 
169
+ elif object_type in {"person", "car"}:
170
+ # --- YOLOv5 for Persons or Cars (filtering for the selected type) ---
171
+ if yolov5_model is None:
172
+ print("DEBUG: YOLOv5 model is not initialized.")
173
+ return None, "YOLOv5 model is not available."
174
+ else:
175
+ try:
176
+ img_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
177
+ yolo_results = yolov5_model(img_rgb)
178
+ df = yolo_results.pandas().xyxy[0]
179
+ for _, row in df.iterrows():
180
+ if row['name'] == object_type:
181
+ xmin = int(row['xmin'])
182
+ ymin = int(row['ymin'])
183
+ xmax = int(row['xmax'])
184
+ ymax = int(row['ymax'])
185
+ conf = row['confidence']
186
+ label = row['name']
187
+ cv2.rectangle(image, (xmin, ymin), (xmax, ymax), (255, 0, 0), 2)
188
+ text = f"{label} ({conf:.2f})"
189
+ (text_width, text_height), baseline = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
190
+ cv2.rectangle(image, (xmin, ymin - text_height - baseline - 5), (xmin + text_width, ymin - 5), (255, 0, 0), -1)
191
+ cv2.putText(image, text, (xmin, ymin - 5 - baseline), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
192
+ detection_info.append({
193
+ "class": label,
194
+ "confidence": f"{conf:.2f}",
195
+ "width_cm": "N/A",
196
+ "height_cm": "N/A"
197
+ })
198
+ except Exception as e:
199
+ print("DEBUG: Error during YOLOv5 inference:", e)
200
+ return None, "Error during YOLOv5 inference."
201
  else:
202
+ err_msg = f"Unrecognized object type: {object_type}"
203
+ print("DEBUG:", err_msg)
204
+ return None, err_msg
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
  # --- Build Top Summary Text ---
207
  detection_counts = Counter(det["class"] for det in detection_info)
 
214
  return image, detection_info
215
 
216
  #########################################
217
+ # 3. HTML Templates
218
  #########################################
219
 
220
+ # --- Landing Page Template (New) ---
221
+ landing_template = '''
222
+ <!DOCTYPE html>
223
+ <html lang="en">
224
+ <head>
225
+ <meta charset="UTF-8">
226
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
227
+ <title>MathLens</title>
228
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
229
+ <style>
230
+ @import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap');
231
+ body {
232
+ background-color: #fff;
233
+ color: #000;
234
+ font-family: "Share Tech Mono", monospace;
235
+ text-align: center;
236
+ display: flex;
237
+ flex-direction: column;
238
+ justify-content: center;
239
+ align-items: center;
240
+ min-height: 100vh;
241
+ padding: 20px;
242
+ overflow: auto;
243
+ }
244
+ h1 {
245
+ font-size: 2.5rem;
246
+ margin-bottom: 20px;
247
+ }
248
+ p {
249
+ font-size: 1.5rem;
250
+ margin-bottom: 40px;
251
+ }
252
+ .btn {
253
+ display: inline-block;
254
+ margin: 10px;
255
+ padding: 15px 30px;
256
+ font-size: 1.2rem;
257
+ text-decoration: none;
258
+ border: 2px solid #000;
259
+ color: #000;
260
+ cursor: pointer;
261
+ transition: background-color 0.3s, color 0.3s;
262
+ }
263
+ .btn:hover {
264
+ background-color: #000;
265
+ color: #fff;
266
+ }
267
+ </style>
268
+ </head>
269
+ <body>
270
+ <h1>MathLens</h1>
271
+ <p>What do you want to count?</p>
272
+ <div>
273
+ <a href="{{ url_for('upload') }}?object_type=person" class="btn">People</a>
274
+ <a href="{{ url_for('upload') }}?object_type=car" class="btn">Cars</a>
275
+ <a href="{{ url_for('upload') }}?object_type=box" class="btn">Boxes</a>
276
+ </div>
277
+ </body>
278
+ </html>
279
+ '''
280
+
281
+ # --- Upload & Result Page Template (Original Design with Home Button and Progress Bar Loading Overlay) ---
282
+ upload_template = '''
283
+ <!DOCTYPE html>
284
  <html lang="en">
285
  <head>
286
  <meta charset="UTF-8">
 
329
  color: #000;
330
  font-family: "Share Tech Mono", monospace;
331
  }
332
+ .home-btn {
333
+ display: inline-block;
334
+ margin: 10px auto 20px;
335
+ padding: 10px 20px;
336
+ border: 2px solid #000;
337
+ color: #000;
338
+ text-decoration: none;
339
+ font-family: "Share Tech Mono", monospace;
340
+ cursor: pointer;
341
+ transition: background-color 0.3s, color 0.3s;
342
+ }
343
+ .home-btn:hover {
344
+ background-color: #000;
345
+ color: #fff;
346
+ }
347
+ /* Progress bar overlay styles */
348
+ #loading {
349
+ position: fixed;
350
+ top: 0;
351
+ left: 0;
352
+ width: 100%;
353
+ height: 100%;
354
+ background: rgba(255,255,255,0.95);
355
+ display: none;
356
+ flex-direction: column;
357
+ align-items: center;
358
+ justify-content: center;
359
+ z-index: 9999;
360
+ }
361
+ #progressContainer {
362
+ width: 80%;
363
+ max-width: 600px;
364
+ background: #ddd;
365
+ border: 2px solid #000;
366
+ border-radius: 5px;
367
+ overflow: hidden;
368
+ margin-bottom: 20px;
369
+ }
370
+ #progressBar {
371
+ width: 0%;
372
+ height: 30px;
373
+ background: #000;
374
+ transition: width 0.2s;
375
+ }
376
+ #progressText {
377
+ font-size: 1.2rem;
378
+ font-weight: bold;
379
+ }
380
  .content-wrapper {
381
  display: flex;
382
  flex-direction: row;
 
425
  </style>
426
  </head>
427
  <body>
428
+ <!-- Home button -->
429
+ <a href="{{ url_for('landing') }}" class="home-btn">Home</a>
430
+ <!-- Progress bar loading overlay -->
431
+ <div id="loading">
432
+ <div id="progressContainer">
433
+ <div id="progressBar"></div>
434
+ </div>
435
+ <div id="progressText">Initializing... πŸ”„</div>
436
+ </div>
437
  <div class="typing-effect" id="typing"></div>
438
+ <!-- onsubmit calls the showLoading() function -->
439
+ <form method="post" enctype="multipart/form-data" onsubmit="showLoading()">
440
  <input type="file" name="file" accept="image/*" required>
441
+ <!-- Hidden field to pass the selected object type -->
442
+ <input type="hidden" name="object_type" value="{{ object_type }}">
443
  <button type="submit">Analyze Image</button>
444
  </form>
445
  {% if image_data or detection_info %}
 
471
  {% endif %}
472
  <div class="footer">&copy; 2024 MathLens AI Detection App. All rights reserved.</div>
473
  <script>
474
+ // Function to show the loading overlay with a progress bar and cool phrases
475
+ function showLoading() {
476
+ document.getElementById("loading").style.display = "flex";
477
+ let progress = 0;
478
+ const progressBar = document.getElementById("progressBar");
479
+ const progressText = document.getElementById("progressText");
480
+ // Array of phrases with their upper progress limits
481
+ const phrases = [
482
+ {limit: 20, text: "Writing scripts... ✍️"},
483
+ {limit: 40, text: "Calculating formulas... πŸ”’"},
484
+ {limit: 60, text: "Mixing up magic... ✨"},
485
+ {limit: 80, text: "Almost there... πŸš€"},
486
+ {limit: 100, text: "Finalizing details... βœ…"}
487
+ ];
488
+ // Update progress every 50ms (simulation)
489
+ const interval = setInterval(() => {
490
+ // Increase progress by a random value (to add some variability)
491
+ progress += Math.floor(Math.random() * 3) + 1;
492
+ if (progress > 100) progress = 100;
493
+ progressBar.style.width = progress + "%";
494
+ // Update phrase based on current progress
495
+ for (let i = 0; i < phrases.length; i++) {
496
+ if (progress <= phrases[i].limit) {
497
+ progressText.textContent = phrases[i].text;
498
+ break;
499
+ }
500
+ }
501
+ // If progress reaches 100, clear the interval
502
+ if (progress >= 100) {
503
+ clearInterval(interval);
504
+ }
505
+ }, 50);
506
+ }
507
+ // Existing typing effect code
508
  const textArray = ["MathLens", "Smart Counting with Maths"];
509
  let textIndex = 0;
510
  let charIndex = 0;
 
533
  </script>
534
  </body>
535
  </html>
536
+ '''
537
 
538
+ #########################################
539
+ # 4. Flask Routes
540
+ #########################################
541
 
542
+ # Landing page: displays only the title and three buttons
543
+ @app.route('/')
544
+ def landing():
545
+ return render_template_string(landing_template)
546
+
547
+ # Upload page: uses the original upload page design and processes the image.
548
+ @app.route('/upload', methods=['GET', 'POST'])
549
+ def upload():
550
+ if request.method == 'GET':
551
+ # Get the selected object type from the query parameter
552
+ object_type = request.args.get('object_type', '').lower()
553
+ if object_type not in {"person", "car", "box"}:
554
+ flash("Please select a valid object type.")
555
+ return redirect(url_for('landing'))
556
+ return render_template_string(upload_template, object_type=object_type)
557
+ else:
558
+ # POST: process the uploaded image
559
+ if 'file' not in request.files:
560
+ flash('No file part')
561
+ return redirect(request.url)
562
+ file = request.files['file']
563
+ if file.filename == '':
564
+ flash('No selected file')
565
+ return redirect(request.url)
566
+ object_type = request.form.get('object_type', '').lower()
567
+ if object_type not in {"person", "car", "box"}:
568
+ flash("Invalid object type selected.")
569
+ return redirect(url_for('landing'))
570
+ upload_path = "uploaded.jpg"
571
+ try:
572
+ file.save(upload_path)
573
+ except Exception as e:
574
+ print("DEBUG: Error saving uploaded file:", e)
575
+ flash("Error saving uploaded file.")
576
+ return redirect(request.url)
577
+ processed_image, detection_info = process_image(upload_path, object_type)
578
+ if processed_image is None:
579
+ flash("Error Processing Image: " + detection_info)
580
+ return redirect(request.url)
581
+ else:
582
+ retval, buffer = cv2.imencode('.jpg', processed_image)
583
+ image_data = base64.b64encode(buffer).decode('utf-8')
584
+ try:
585
+ os.remove(upload_path)
586
+ except Exception as e:
587
+ print("DEBUG: Error removing uploaded file:", e)
588
+ return render_template_string(upload_template, object_type=object_type,
589
+ image_data=image_data, detection_info=detection_info)
590
 
591
  #########################################
592
+ # 5. Run the App
593
  #########################################
594
 
595
  if __name__ == '__main__':
596
+ # Runs on host 0.0.0.0 and port 7860 (adjust as needed)
597
  app.run(host="0.0.0.0", port=7860)