Manvikk commited on
Commit
bd8ee97
·
verified ·
1 Parent(s): b97ed56

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +15 -27
app.py CHANGED
@@ -75,7 +75,8 @@ def custom_nms(preds, iou_threshold=0.3):
75
  filtered_preds.append(pred)
76
  return filtered_preds
77
 
78
- def process_image(job_id, image_path, object_type):
 
79
  try:
80
  jobs[job_id]['progress'] = 10
81
  # Load image
@@ -87,8 +88,8 @@ def process_image(job_id, image_path, object_type):
87
 
88
  jobs[job_id]['progress'] = 20
89
  img_height, img_width = image.shape[:2]
90
- # Compute dynamic thickness based on image size.
91
- thickness = max(2, int(min(img_width, img_height) / 300))
92
  detection_info = []
93
 
94
  if object_type == "box":
@@ -128,22 +129,18 @@ def process_image(job_id, image_path, object_type):
128
  jobs[job_id]['progress'] = 60
129
 
130
  # (Optional) Detect ArUco marker for measurement
131
- # IMPORTANT: Set this to your marker's real-world side length in centimeters.
132
  marker_real_width_cm = 10.0
133
  try:
134
  gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
135
  aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_6X6_250)
136
- # Use the newer DetectorParameters_create if available
137
  if hasattr(cv2.aruco, 'DetectorParameters_create'):
138
  aruco_params = cv2.aruco.DetectorParameters_create()
139
  else:
140
  aruco_params = cv2.aruco.DetectorParameters()
141
  corners, ids, _ = cv2.aruco.detectMarkers(gray, aruco_dict, parameters=aruco_params)
142
  if ids is not None and len(corners) > 0:
143
- # Use the first detected marker as reference
144
  marker_corners = corners[0].reshape((4, 2))
145
  cv2.aruco.drawDetectedMarkers(image, corners, ids)
146
- # Compute the distance between consecutive corners and take the average
147
  side_lengths = []
148
  for i in range(4):
149
  pt1 = marker_corners[i]
@@ -160,7 +157,6 @@ def process_image(job_id, image_path, object_type):
160
  x1, y1, x2, y2 = pred["box"]
161
  label = pred["class"]
162
  confidence = pred["confidence"]
163
- # Use dynamic thickness here
164
  cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), thickness)
165
  if conversion_factor is not None:
166
  box_width_pixels = x2 - x1
@@ -201,7 +197,6 @@ def process_image(job_id, image_path, object_type):
201
  ymax = int(row['ymax'])
202
  conf = row['confidence']
203
  label = row['name']
204
- # Use dynamic thickness here as well
205
  cv2.rectangle(image, (xmin, ymin), (xmax, ymax), (255, 0, 0), thickness)
206
  text = f"{label} ({conf:.2f})"
207
  (text_width, text_height), baseline = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
@@ -334,8 +329,9 @@ upload_template = '''
334
  <!-- The file upload form (submission handled via AJAX) -->
335
  <form id="uploadForm">
336
  <input type="file" name="file" accept="image/*" required>
337
- <!-- Hidden field to pass the selected object type -->
338
  <input type="hidden" name="object_type" value="{{ object_type }}">
 
339
  <button type="submit">Analyze Image</button>
340
  </form>
341
  <div id="resultContainer">
@@ -392,6 +388,9 @@ upload_template = '''
392
  }
393
  document.addEventListener("DOMContentLoaded", () => {
394
  setTimeout(typeEffect, 500);
 
 
 
395
  });
396
  // AJAX-based submission and progress polling
397
  const uploadForm = document.getElementById("uploadForm");
@@ -415,7 +414,6 @@ upload_template = '''
415
  .then(progData => {
416
  const progress = progData.progress;
417
  document.getElementById("progressBar").style.width = progress + "%";
418
- // Update dynamic phrases based on progress
419
  if (progress < 10) {
420
  document.getElementById("progressText").textContent = "Starting up... 🛠️";
421
  } else if (progress < 30) {
@@ -431,12 +429,10 @@ upload_template = '''
431
  }
432
  if (progress >= 100) {
433
  clearInterval(progressInterval);
434
- // Once done, fetch the result and update the page
435
  fetch("{{ url_for('result') }}?job_id=" + jobId)
436
  .then(response => response.json())
437
  .then(resultData => {
438
  document.getElementById("progressOverlay").style.display = "none";
439
- // Replace the result container with the new result
440
  document.getElementById("resultContainer").innerHTML = `
441
  <div class="content-wrapper">
442
  <img src="data:image/jpeg;base64,${resultData.image_data}" alt="Processed Image" class="result-img">
@@ -482,25 +478,20 @@ upload_template = '''
482
  # 4. Flask Routes
483
  #########################################
484
 
485
- # Landing page
486
  @app.route('/')
487
  def landing():
488
  return render_template_string(landing_template)
489
 
490
- # Traditional upload page (in case you want to support non-AJAX)
491
  @app.route('/upload', methods=['GET'])
492
  def upload():
493
  object_type = request.args.get('object_type', '').lower()
494
  if object_type not in {"person", "car", "box"}:
495
  flash("Please select a valid object type.")
496
  return redirect(url_for('landing'))
497
- # In this version the AJAX version is preferred.
498
  return render_template_string(upload_template, object_type=object_type)
499
 
500
- # Endpoint to start analysis (called via AJAX)
501
  @app.route('/analyze', methods=['POST'])
502
  def analyze():
503
- # Save uploaded file
504
  if 'file' not in request.files:
505
  return jsonify({"error": "No file provided."}), 400
506
  file = request.files['file']
@@ -509,23 +500,23 @@ def analyze():
509
  object_type = request.form.get('object_type', '').lower()
510
  if object_type not in {"person", "car", "box"}:
511
  return jsonify({"error": "Invalid object type."}), 400
 
 
 
 
 
512
  upload_path = "uploaded.jpg"
513
  try:
514
  file.save(upload_path)
515
  except Exception as e:
516
  return jsonify({"error": "Error saving file."}), 500
517
 
518
- # Create a unique job id and initialize job progress
519
  job_id = str(uuid.uuid4())
520
  jobs[job_id] = {"progress": 0, "result": None}
521
- # Start background thread to process image
522
- thread = threading.Thread(target=process_image, args=(job_id, upload_path, object_type))
523
  thread.start()
524
- # Optionally remove the uploaded file after starting the thread
525
- # (In a real system, you might want to keep it until processing is done.)
526
  return jsonify({"job_id": job_id})
527
 
528
- # Endpoint for client to poll progress
529
  @app.route('/progress', methods=['GET'])
530
  def progress():
531
  job_id = request.args.get('job_id', '')
@@ -533,14 +524,12 @@ def progress():
533
  return jsonify({"progress": 0})
534
  return jsonify({"progress": jobs[job_id].get("progress", 0)})
535
 
536
- # Endpoint for client to fetch final result
537
  @app.route('/result', methods=['GET'])
538
  def result():
539
  job_id = request.args.get('job_id', '')
540
  if job_id not in jobs or jobs[job_id].get("result") is None:
541
  return jsonify({"error": "Result not available."}), 404
542
  result = jobs[job_id]["result"]
543
- # Clean up job if desired
544
  del jobs[job_id]
545
  return jsonify(result)
546
 
@@ -549,5 +538,4 @@ def result():
549
  #########################################
550
 
551
  if __name__ == '__main__':
552
- # Run in threaded mode so background threads can work
553
  app.run(host="0.0.0.0", port=7860, threaded=True)
 
75
  filtered_preds.append(pred)
76
  return filtered_preds
77
 
78
+ # Note the added multiplier parameter.
79
+ def process_image(job_id, image_path, object_type, multiplier):
80
  try:
81
  jobs[job_id]['progress'] = 10
82
  # Load image
 
88
 
89
  jobs[job_id]['progress'] = 20
90
  img_height, img_width = image.shape[:2]
91
+ # Compute dynamic thickness based on image size and multiplier.
92
+ thickness = max(2, int(min(img_width, img_height) / 300)) * multiplier
93
  detection_info = []
94
 
95
  if object_type == "box":
 
129
  jobs[job_id]['progress'] = 60
130
 
131
  # (Optional) Detect ArUco marker for measurement
 
132
  marker_real_width_cm = 10.0
133
  try:
134
  gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
135
  aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_6X6_250)
 
136
  if hasattr(cv2.aruco, 'DetectorParameters_create'):
137
  aruco_params = cv2.aruco.DetectorParameters_create()
138
  else:
139
  aruco_params = cv2.aruco.DetectorParameters()
140
  corners, ids, _ = cv2.aruco.detectMarkers(gray, aruco_dict, parameters=aruco_params)
141
  if ids is not None and len(corners) > 0:
 
142
  marker_corners = corners[0].reshape((4, 2))
143
  cv2.aruco.drawDetectedMarkers(image, corners, ids)
 
144
  side_lengths = []
145
  for i in range(4):
146
  pt1 = marker_corners[i]
 
157
  x1, y1, x2, y2 = pred["box"]
158
  label = pred["class"]
159
  confidence = pred["confidence"]
 
160
  cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), thickness)
161
  if conversion_factor is not None:
162
  box_width_pixels = x2 - x1
 
197
  ymax = int(row['ymax'])
198
  conf = row['confidence']
199
  label = row['name']
 
200
  cv2.rectangle(image, (xmin, ymin), (xmax, ymax), (255, 0, 0), thickness)
201
  text = f"{label} ({conf:.2f})"
202
  (text_width, text_height), baseline = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
 
329
  <!-- The file upload form (submission handled via AJAX) -->
330
  <form id="uploadForm">
331
  <input type="file" name="file" accept="image/*" required>
332
+ <!-- Hidden fields to pass the selected object type and device multiplier -->
333
  <input type="hidden" name="object_type" value="{{ object_type }}">
334
+ <input type="hidden" name="multiplier" id="multiplier" value="1">
335
  <button type="submit">Analyze Image</button>
336
  </form>
337
  <div id="resultContainer">
 
388
  }
389
  document.addEventListener("DOMContentLoaded", () => {
390
  setTimeout(typeEffect, 500);
391
+ // Detect if the device is mobile and update the thickness multiplier accordingly.
392
+ var isMobile = /Mobi|Android/i.test(navigator.userAgent);
393
+ document.getElementById("multiplier").value = isMobile ? 2 : 1;
394
  });
395
  // AJAX-based submission and progress polling
396
  const uploadForm = document.getElementById("uploadForm");
 
414
  .then(progData => {
415
  const progress = progData.progress;
416
  document.getElementById("progressBar").style.width = progress + "%";
 
417
  if (progress < 10) {
418
  document.getElementById("progressText").textContent = "Starting up... 🛠️";
419
  } else if (progress < 30) {
 
429
  }
430
  if (progress >= 100) {
431
  clearInterval(progressInterval);
 
432
  fetch("{{ url_for('result') }}?job_id=" + jobId)
433
  .then(response => response.json())
434
  .then(resultData => {
435
  document.getElementById("progressOverlay").style.display = "none";
 
436
  document.getElementById("resultContainer").innerHTML = `
437
  <div class="content-wrapper">
438
  <img src="data:image/jpeg;base64,${resultData.image_data}" alt="Processed Image" class="result-img">
 
478
  # 4. Flask Routes
479
  #########################################
480
 
 
481
  @app.route('/')
482
  def landing():
483
  return render_template_string(landing_template)
484
 
 
485
  @app.route('/upload', methods=['GET'])
486
  def upload():
487
  object_type = request.args.get('object_type', '').lower()
488
  if object_type not in {"person", "car", "box"}:
489
  flash("Please select a valid object type.")
490
  return redirect(url_for('landing'))
 
491
  return render_template_string(upload_template, object_type=object_type)
492
 
 
493
  @app.route('/analyze', methods=['POST'])
494
  def analyze():
 
495
  if 'file' not in request.files:
496
  return jsonify({"error": "No file provided."}), 400
497
  file = request.files['file']
 
500
  object_type = request.form.get('object_type', '').lower()
501
  if object_type not in {"person", "car", "box"}:
502
  return jsonify({"error": "Invalid object type."}), 400
503
+ # Retrieve the multiplier sent from the client
504
+ try:
505
+ multiplier = int(request.form.get('multiplier', 1))
506
+ except ValueError:
507
+ multiplier = 1
508
  upload_path = "uploaded.jpg"
509
  try:
510
  file.save(upload_path)
511
  except Exception as e:
512
  return jsonify({"error": "Error saving file."}), 500
513
 
 
514
  job_id = str(uuid.uuid4())
515
  jobs[job_id] = {"progress": 0, "result": None}
516
+ thread = threading.Thread(target=process_image, args=(job_id, upload_path, object_type, multiplier))
 
517
  thread.start()
 
 
518
  return jsonify({"job_id": job_id})
519
 
 
520
  @app.route('/progress', methods=['GET'])
521
  def progress():
522
  job_id = request.args.get('job_id', '')
 
524
  return jsonify({"progress": 0})
525
  return jsonify({"progress": jobs[job_id].get("progress", 0)})
526
 
 
527
  @app.route('/result', methods=['GET'])
528
  def result():
529
  job_id = request.args.get('job_id', '')
530
  if job_id not in jobs or jobs[job_id].get("result") is None:
531
  return jsonify({"error": "Result not available."}), 404
532
  result = jobs[job_id]["result"]
 
533
  del jobs[job_id]
534
  return jsonify(result)
535
 
 
538
  #########################################
539
 
540
  if __name__ == '__main__':
 
541
  app.run(host="0.0.0.0", port=7860, threaded=True)