mchi8sp2 commited on
Commit
49fe77c
·
verified ·
1 Parent(s): 7be9574

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +112 -31
app.py CHANGED
@@ -6,41 +6,82 @@ import torch
6
  import time
7
  import numpy as np
8
  import uuid
9
-
 
10
  from transformers import RTDetrForObjectDetection, RTDetrImageProcessor
11
-
12
  from draw_boxes import draw_bounding_boxes
13
 
 
 
 
14
  image_processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_r50vd")
15
  model = RTDetrForObjectDetection.from_pretrained("PekingU/rtdetr_r50vd").to("cuda")
16
 
17
-
18
  SUBSAMPLE = 2
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  @spaces.GPU
21
  def stream_object_detection(video, conf_threshold):
22
  cap = cv2.VideoCapture(video)
23
 
24
- video_codec = cv2.VideoWriter_fourcc(*"mp4v") # type: ignore
25
- fps = int(cap.get(cv2.CAP_PROP_FPS))
26
 
27
- desired_fps = fps // SUBSAMPLE
28
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) // 2
29
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) // 2
 
 
30
 
31
  iterating, frame = cap.read()
32
-
33
  n_frames = 0
 
34
 
 
35
  name = f"output_{uuid.uuid4()}.mp4"
36
- segment_file = cv2.VideoWriter(name, video_codec, desired_fps, (width, height)) # type: ignore
37
- batch = []
 
 
38
 
39
  while iterating:
40
- frame = cv2.resize( frame, (0,0), fx=0.5, fy=0.5)
41
  frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
 
42
  if n_frames % SUBSAMPLE == 0:
43
  batch.append(frame)
 
44
  if len(batch) == 2 * desired_fps:
45
  inputs = image_processor(images=batch, return_tensors="pt").to("cuda")
46
 
@@ -55,40 +96,76 @@ def stream_object_detection(video, conf_threshold):
55
  boxes = image_processor.post_process_object_detection(
56
  outputs,
57
  target_sizes=torch.tensor([(height, width)] * len(batch)),
58
- threshold=conf_threshold)
59
-
60
- for i, (array, box) in enumerate(zip(batch, boxes)):
61
- pil_image = draw_bounding_boxes(Image.fromarray(array), box, model, conf_threshold)
62
- frame = np.array(pil_image)
63
- # Convert RGB to BGR
64
- frame = frame[:, :, ::-1].copy()
65
- segment_file.write(frame)
 
66
 
67
  batch = []
68
  segment_file.release()
69
- yield name
 
 
 
 
70
  end = time.time()
71
  print("time taken for processing boxes", end - start)
 
 
72
  name = f"output_{uuid.uuid4()}.mp4"
73
- segment_file = cv2.VideoWriter(name, video_codec, desired_fps, (width, height)) # type: ignore
74
 
75
  iterating, frame = cap.read()
76
  n_frames += 1
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
 
 
 
 
79
  with gr.Blocks() as app:
80
  gr.HTML(
81
  """
82
- <h1 style='text-align: center'>
83
- Video Object Detection with RT-DETR
84
- </h1>
85
- """)
 
86
  gr.HTML(
87
  """
88
  <h3 style='text-align: center'>
89
- <a href='https://arxiv.org/abs/2304.08069' target='_blank'>arXiv</a> | <a href='https://huggingface.co/PekingU/rtdetr_r101vd_coco_o365' target='_blank'>github</a>
 
90
  </h3>
91
- """)
 
92
  with gr.Row():
93
  with gr.Column():
94
  video = gr.Video(label="Video Source")
@@ -100,7 +177,11 @@ with gr.Blocks() as app:
100
  value=0.30,
101
  )
102
  with gr.Column():
103
- output_video = gr.Video(label="Processed Video", streaming=True, autoplay=True)
 
 
 
 
104
 
105
  video.upload(
106
  fn=stream_object_detection,
@@ -108,5 +189,5 @@ with gr.Blocks() as app:
108
  outputs=[output_video],
109
  )
110
 
111
- if __name__ == '__main__':
112
- app.launch()
 
6
  import time
7
  import numpy as np
8
  import uuid
9
+ import os
10
+ import subprocess
11
  from transformers import RTDetrForObjectDetection, RTDetrImageProcessor
 
12
  from draw_boxes import draw_bounding_boxes
13
 
14
+ # -------------------------------------------------
15
+ # Model loading (same as original)
16
+ # -------------------------------------------------
17
  image_processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_r50vd")
18
  model = RTDetrForObjectDetection.from_pretrained("PekingU/rtdetr_r50vd").to("cuda")
19
 
 
20
  SUBSAMPLE = 2
21
 
22
+
23
+ def reencode_to_browser_h264(input_path: str) -> str:
24
+ """
25
+ Force a browser-compatible H.264 stream.
26
+ - libx264
27
+ - yuv420p (mandatory for Chrome/Firefox/Safari)
28
+ - +faststart (moov atom at the beginning → progressive playback)
29
+ """
30
+ output_path = input_path.replace(".mp4", "_h264.mp4")
31
+ cmd = [
32
+ "ffmpeg", "-y",
33
+ "-i", input_path,
34
+ "-c:v", "libx264",
35
+ "-preset", "veryfast",
36
+ "-crf", "23",
37
+ "-pix_fmt", "yuv420p",
38
+ "-movflags", "+faststart",
39
+ "-an", # no audio
40
+ output_path
41
+ ]
42
+ subprocess.run(
43
+ cmd,
44
+ check=True,
45
+ stdout=subprocess.DEVNULL,
46
+ stderr=subprocess.DEVNULL
47
+ )
48
+ # Clean intermediate file
49
+ if os.path.exists(input_path):
50
+ os.remove(input_path)
51
+ return output_path
52
+
53
+
54
  @spaces.GPU
55
  def stream_object_detection(video, conf_threshold):
56
  cap = cv2.VideoCapture(video)
57
 
58
+ fps = int(cap.get(cv2.CAP_PROP_FPS)) or 30
59
+ desired_fps = max(1, fps // SUBSAMPLE)
60
 
61
+ # Force even dimensions (H.264 requirement)
62
+ orig_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
63
+ orig_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
64
+ width = (orig_w // 2) // 2 * 2
65
+ height = (orig_h // 2) // 2 * 2
66
 
67
  iterating, frame = cap.read()
 
68
  n_frames = 0
69
+ batch = []
70
 
71
+ # First segment
72
  name = f"output_{uuid.uuid4()}.mp4"
73
+ # We deliberately use mp4v for writing reliability,
74
+ # then re-encode. This is the most robust pattern on HF Spaces.
75
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v")
76
+ segment_file = cv2.VideoWriter(name, fourcc, desired_fps, (width, height))
77
 
78
  while iterating:
79
+ frame = cv2.resize(frame, (width, height))
80
  frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
81
+
82
  if n_frames % SUBSAMPLE == 0:
83
  batch.append(frame)
84
+
85
  if len(batch) == 2 * desired_fps:
86
  inputs = image_processor(images=batch, return_tensors="pt").to("cuda")
87
 
 
96
  boxes = image_processor.post_process_object_detection(
97
  outputs,
98
  target_sizes=torch.tensor([(height, width)] * len(batch)),
99
+ threshold=conf_threshold
100
+ )
101
+
102
+ for array, box in zip(batch, boxes):
103
+ pil_image = draw_bounding_boxes(
104
+ Image.fromarray(array), box, model, conf_threshold
105
+ )
106
+ frame_bgr = np.array(pil_image)[:, :, ::-1].copy()
107
+ segment_file.write(frame_bgr)
108
 
109
  batch = []
110
  segment_file.release()
111
+
112
+ # === Critical fix: convert to real H.264 before yielding ===
113
+ playable_name = reencode_to_browser_h264(name)
114
+ yield playable_name
115
+
116
  end = time.time()
117
  print("time taken for processing boxes", end - start)
118
+
119
+ # Prepare next segment
120
  name = f"output_{uuid.uuid4()}.mp4"
121
+ segment_file = cv2.VideoWriter(name, fourcc, desired_fps, (width, height))
122
 
123
  iterating, frame = cap.read()
124
  n_frames += 1
125
 
126
+ # Flush remaining frames if any
127
+ if batch:
128
+ inputs = image_processor(images=batch, return_tensors="pt").to("cuda")
129
+ with torch.no_grad():
130
+ outputs = model(**inputs)
131
+ boxes = image_processor.post_process_object_detection(
132
+ outputs,
133
+ target_sizes=torch.tensor([(height, width)] * len(batch)),
134
+ threshold=conf_threshold
135
+ )
136
+ for array, box in zip(batch, boxes):
137
+ pil_image = draw_bounding_boxes(
138
+ Image.fromarray(array), box, model, conf_threshold
139
+ )
140
+ frame_bgr = np.array(pil_image)[:, :, ::-1].copy()
141
+ segment_file.write(frame_bgr)
142
+
143
+ segment_file.release()
144
+ playable_name = reencode_to_browser_h264(name)
145
+ yield playable_name
146
+
147
+ cap.release()
148
 
149
+
150
+ # -------------------------------------------------
151
+ # Gradio UI (unchanged structure)
152
+ # -------------------------------------------------
153
  with gr.Blocks() as app:
154
  gr.HTML(
155
  """
156
+ <h1 style='text-align: center'>
157
+ Video Object Detection with RT-DETR (Browser-Compatible Fix)
158
+ </h1>
159
+ """
160
+ )
161
  gr.HTML(
162
  """
163
  <h3 style='text-align: center'>
164
+ <a href='https://arxiv.org/abs/2304.08069' target='_blank'>arXiv</a> |
165
+ <a href='https://huggingface.co/PekingU/rtdetr_r101vd_coco_o365' target='_blank'>Model</a>
166
  </h3>
167
+ """
168
+ )
169
  with gr.Row():
170
  with gr.Column():
171
  video = gr.Video(label="Video Source")
 
177
  value=0.30,
178
  )
179
  with gr.Column():
180
+ output_video = gr.Video(
181
+ label="Processed Video",
182
+ streaming=True,
183
+ autoplay=True
184
+ )
185
 
186
  video.upload(
187
  fn=stream_object_detection,
 
189
  outputs=[output_video],
190
  )
191
 
192
+ if __name__ == "__main__":
193
+ app.launch()