PrashanthB461 commited on
Commit
a277b4f
·
verified ·
1 Parent(s): 02b45d6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -19
app.py CHANGED
@@ -209,14 +209,14 @@ CONFIG = {
209
  "WORKER_TRACKING_DURATION": 5.0,
210
  "MAX_PROCESSING_TIME": 60,
211
  "FRAME_SKIP": 1,
212
- "BATCH_SIZE": 4, # Reduced to 4 to lower memory usage
213
  "PARALLEL_WORKERS": max(1, cpu_count() - 1),
214
  "TRACK_BUFFER": 90,
215
  "TRACK_THRESH": 0.3,
216
  "MATCH_THRESH": 0.5,
217
  "SNAPSHOT_QUALITY": 95,
218
  "MAX_WORKER_DISTANCE": 300,
219
- "TARGET_RESOLUTION": (640, 360) # Added to resize frames
220
  }
221
 
222
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
@@ -235,7 +235,6 @@ def load_model():
235
  torch.hub.download_url_to_file('https://github.com/ultralytics/assets/releases/download/v8.3.0/yolov8n.pt', model_path)
236
 
237
  model = YOLO(model_path).to(device)
238
- # Enable FP16 inference if on GPU
239
  if device.type == "cuda":
240
  model.model.half()
241
  logger.info(f"Model classes: {model.names}")
@@ -247,12 +246,56 @@ def load_model():
247
  model = load_model()
248
 
249
  # ========================== # Helper Functions # ==========================
250
- def preprocess_frame(frame):
251
- # Resize frame to target resolution
252
- target_res = CONFIG["TARGET_RESOLUTION"]
253
- frame = cv2.resize(frame, target_res, interpolation=cv2.INTER_LINEAR)
254
- frame = cv2.convertScaleAbs(frame, alpha=1.2, beta=20)
255
- return frame
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
 
257
  def draw_detections(frame, detections):
258
  result_frame = frame.copy()
@@ -453,7 +496,7 @@ def push_report_to_salesforce(violations, score, pdf_path, pdf_file):
453
 
454
  try:
455
  record = sf.Safety_Video_Report__c.create(record_data)
456
- logger.info(f"Created Safety_Video_Report__c record: {record['id']}")
457
  except Exception as e:
458
  logger.error(f"Failed to create Safety_Video_Report__c: {e}")
459
  record = sf.Account.create({"Name": f"Safety_Report_{int(time.time())}"})
@@ -558,6 +601,8 @@ def process_video(video_data, temp_dir):
558
  while processed_frames < total_frames:
559
  batch_frames = []
560
  batch_indices = []
 
 
561
 
562
  for _ in range(CONFIG["BATCH_SIZE"]):
563
  frame_idx = int(cap.get(cv2.CAP_PROP_POS_FRAMES))
@@ -569,7 +614,9 @@ def process_video(video_data, temp_dir):
569
  logger.warning(f"Failed to read frame {frame_idx}. Skipping.")
570
  break
571
 
572
- frame = preprocess_frame(frame)
 
 
573
 
574
  for _ in range(frame_skip - 1):
575
  if not cap.grab():
@@ -577,6 +624,8 @@ def process_video(video_data, temp_dir):
577
 
578
  batch_frames.append(frame)
579
  batch_indices.append(frame_idx)
 
 
580
  processed_frames += 1
581
 
582
  if not batch_frames:
@@ -595,15 +644,14 @@ def process_video(video_data, temp_dir):
595
  logger.error(f"Model inference failed: {e}")
596
  raise ValueError(f"Failed to process video frames with YOLO model: {str(e)}")
597
  finally:
598
- # Clear memory
599
- batch_frames = [] # Clear the list to free memory
600
  if device.type == "cuda":
601
  torch.cuda.empty_cache()
602
 
603
- for i, (result, frame_idx) in enumerate(zip(results, batch_indices)):
604
  current_time = frame_idx / fps
605
 
606
- if time.time() - start_time > 0.5: # Yield more frequently
607
  progress = (processed_frames / total_frames) * 100
608
  elapsed_time = time.time() - start_time
609
  fps_processed = processed_frames / elapsed_time if elapsed_time > 0 else 0
@@ -625,6 +673,8 @@ def process_video(video_data, temp_dir):
625
  continue
626
 
627
  bbox = box.xywh.cpu().numpy()[0]
 
 
628
  track_inputs.append({
629
  "bbox": bbox,
630
  "conf": conf,
@@ -669,8 +719,8 @@ def process_video(video_data, temp_dir):
669
  "timestamp": current_time
670
  }
671
 
672
- snapshot_frame = batch_frames_tensor[i].cpu().numpy().transpose(1, 2, 0) * 255
673
- snapshot_frame = snapshot_frame.astype(np.uint8)
674
  snapshot_frame = draw_detections(snapshot_frame, [detection])
675
 
676
  cv2.putText(
@@ -703,9 +753,8 @@ def process_video(video_data, temp_dir):
703
 
704
  logger.info(f"Captured snapshot for {label} violation by worker {worker_id} at {current_time:.2f}s")
705
 
706
- # Clear snapshots periodically to reduce memory usage
707
  if len(snapshots) > 100:
708
- snapshots = snapshots[-10:] # Keep only the last 10 snapshots in memory
709
 
710
  cap.release()
711
  processing_time = time.time() - start_time
 
209
  "WORKER_TRACKING_DURATION": 5.0,
210
  "MAX_PROCESSING_TIME": 60,
211
  "FRAME_SKIP": 1,
212
+ "BATCH_SIZE": 4,
213
  "PARALLEL_WORKERS": max(1, cpu_count() - 1),
214
  "TRACK_BUFFER": 90,
215
  "TRACK_THRESH": 0.3,
216
  "MATCH_THRESH": 0.5,
217
  "SNAPSHOT_QUALITY": 95,
218
  "MAX_WORKER_DISTANCE": 300,
219
+ "MODEL_INPUT_SIZE": (640, 640) # Updated to match YOLO input requirements
220
  }
221
 
222
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
 
235
  torch.hub.download_url_to_file('https://github.com/ultralytics/assets/releases/download/v8.3.0/yolov8n.pt', model_path)
236
 
237
  model = YOLO(model_path).to(device)
 
238
  if device.type == "cuda":
239
  model.model.half()
240
  logger.info(f"Model classes: {model.names}")
 
246
  model = load_model()
247
 
248
  # ========================== # Helper Functions # ==========================
249
+ def preprocess_frame(frame, original_shape):
250
+ # Resize while preserving aspect ratio, then pad to MODEL_INPUT_SIZE (640x640)
251
+ target_size = CONFIG["MODEL_INPUT_SIZE"] # (640, 640)
252
+ h, w = frame.shape[:2]
253
+ scale = min(target_size[0] / w, target_size[1] / h)
254
+ new_w, new_h = int(w * scale), int(h * scale)
255
+
256
+ # Resize the frame
257
+ frame_resized = cv2.resize(frame, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
258
+
259
+ # Create a new 640x640 image with padding
260
+ padded_frame = np.zeros((target_size[1], target_size[0], 3), dtype=np.uint8)
261
+ top = (target_size[1] - new_h) // 2
262
+ left = (target_size[0] - new_w) // 2
263
+ padded_frame[top:top+new_h, left:left+new_w] = frame_resized
264
+
265
+ # Apply contrast adjustment
266
+ padded_frame = cv2.convertScaleAbs(padded_frame, alpha=1.2, beta=20)
267
+
268
+ # Store padding info to adjust bounding boxes later
269
+ padding_info = {
270
+ "scale": scale,
271
+ "top": top,
272
+ "left": left,
273
+ "original_shape": original_shape
274
+ }
275
+
276
+ return padded_frame, padding_info
277
+
278
+ def adjust_bbox(bbox, padding_info):
279
+ # Adjust bounding box coordinates from padded 640x640 space back to original frame space
280
+ scale = padding_info["scale"]
281
+ top = padding_info["top"]
282
+ left = padding_info["left"]
283
+
284
+ x, y, w, h = bbox
285
+ # Remove padding offset and scale back
286
+ x = (x - left) / scale
287
+ y = (y - top) / scale
288
+ w = w / scale
289
+ h = h / scale
290
+
291
+ # Ensure coordinates are within original frame bounds
292
+ orig_h, orig_w = padding_info["original_shape"][:2]
293
+ x = max(0, min(x, orig_w))
294
+ y = max(0, min(y, orig_h))
295
+ w = max(0, min(w, orig_w - x))
296
+ h = max(0, min(h, orig_h - y))
297
+
298
+ return [x, y, w, h]
299
 
300
  def draw_detections(frame, detections):
301
  result_frame = frame.copy()
 
496
 
497
  try:
498
  record = sf.Safety_Video_Report__c.create(record_data)
499
+ logger.info(f"Created Safety_Violation_Report__c record: {record['id']}")
500
  except Exception as e:
501
  logger.error(f"Failed to create Safety_Video_Report__c: {e}")
502
  record = sf.Account.create({"Name": f"Safety_Report_{int(time.time())}"})
 
601
  while processed_frames < total_frames:
602
  batch_frames = []
603
  batch_indices = []
604
+ batch_padding_info = []
605
+ batch_original_frames = []
606
 
607
  for _ in range(CONFIG["BATCH_SIZE"]):
608
  frame_idx = int(cap.get(cv2.CAP_PROP_POS_FRAMES))
 
614
  logger.warning(f"Failed to read frame {frame_idx}. Skipping.")
615
  break
616
 
617
+ # Keep a copy of the original frame for drawing detections
618
+ original_frame = frame.copy()
619
+ frame, padding_info = preprocess_frame(frame, original_shape=frame.shape)
620
 
621
  for _ in range(frame_skip - 1):
622
  if not cap.grab():
 
624
 
625
  batch_frames.append(frame)
626
  batch_indices.append(frame_idx)
627
+ batch_padding_info.append(padding_info)
628
+ batch_original_frames.append(original_frame)
629
  processed_frames += 1
630
 
631
  if not batch_frames:
 
644
  logger.error(f"Model inference failed: {e}")
645
  raise ValueError(f"Failed to process video frames with YOLO model: {str(e)}")
646
  finally:
647
+ batch_frames = []
 
648
  if device.type == "cuda":
649
  torch.cuda.empty_cache()
650
 
651
+ for i, (result, frame_idx, padding_info, original_frame) in enumerate(zip(results, batch_indices, batch_padding_info, batch_original_frames)):
652
  current_time = frame_idx / fps
653
 
654
+ if time.time() - start_time > 0.5:
655
  progress = (processed_frames / total_frames) * 100
656
  elapsed_time = time.time() - start_time
657
  fps_processed = processed_frames / elapsed_time if elapsed_time > 0 else 0
 
673
  continue
674
 
675
  bbox = box.xywh.cpu().numpy()[0]
676
+ # Adjust bounding box coordinates to original frame space
677
+ bbox = adjust_bbox(bbox, padding_info)
678
  track_inputs.append({
679
  "bbox": bbox,
680
  "conf": conf,
 
719
  "timestamp": current_time
720
  }
721
 
722
+ # Use the original frame for drawing detections
723
+ snapshot_frame = original_frame.copy()
724
  snapshot_frame = draw_detections(snapshot_frame, [detection])
725
 
726
  cv2.putText(
 
753
 
754
  logger.info(f"Captured snapshot for {label} violation by worker {worker_id} at {current_time:.2f}s")
755
 
 
756
  if len(snapshots) > 100:
757
+ snapshots = snapshots[-10:]
758
 
759
  cap.release()
760
  processing_time = time.time() - start_time