srivatsavdamaraju commited on
Commit
09bda2c
Β·
verified Β·
1 Parent(s): 39421c7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +112 -44
app.py CHANGED
@@ -6,6 +6,8 @@ import threading
6
  import time
7
  import mediapipe as mp
8
  import pandas as pd
 
 
9
 
10
  # === Setup ===
11
  OUTPUT_DIR = "captured_frames"
@@ -17,10 +19,41 @@ pose = mp.solutions.pose.Pose()
17
  state = {
18
  "cap": None,
19
  "frame": None,
 
20
  "play": False,
21
- "video_path": None
 
 
22
  }
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  # === Load Video ===
25
  def load_video(video_file):
26
  try:
@@ -34,7 +67,12 @@ def load_video(video_file):
34
  state["cap"].release()
35
 
36
  state["cap"] = cv2.VideoCapture(video_path)
 
 
 
 
37
  state["frame"] = None
 
38
  state["play"] = False
39
  return "βœ… Video loaded successfully!"
40
  except Exception as e:
@@ -52,10 +90,19 @@ def play_video():
52
  if not ret:
53
  state["play"] = False
54
  break
 
 
55
  state["frame"] = frame
56
- time.sleep(0.1)
57
-
58
- threading.Thread(target=stream).start()
 
 
 
 
 
 
 
59
  return "▢️ Playing..."
60
 
61
  # === Pause playback ===
@@ -65,40 +112,35 @@ def pause_video():
65
 
66
  # === Show current frame ===
67
  def show_frame():
68
- if state["frame"] is not None:
69
- return state["frame"][:, :, ::-1] # BGR to RGB
70
  return None
71
 
72
- # === Capture frame (auto-pause) ===
73
  def capture_frame(caption):
74
  if state["frame"] is None:
75
  return "⚠️ No frame to capture.", None
76
 
77
- # Auto-pause video
78
  state["play"] = False
79
-
80
- frame = state["frame"]
 
 
 
 
81
  filename = f"{uuid.uuid4().hex[:8]}.jpg"
82
  path = os.path.join(OUTPUT_DIR, filename)
83
- cv2.imwrite(path, frame)
84
-
85
- # Pose estimation
86
- frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
87
- results = pose.process(frame_rgb)
88
-
89
- coords = []
90
- if results.pose_landmarks:
91
- for lm in results.pose_landmarks.landmark:
92
- coords.append((round(lm.x, 5), round(lm.y, 5), round(lm.z, 5)))
93
-
94
- global df
95
- df = pd.concat([df, pd.DataFrame([{
96
- "filename": filename,
97
- "caption": caption,
98
- "pose_coords": coords
99
- }])], ignore_index=True)
100
 
101
- return f"βœ… Captured & paused: {filename}", frame[:, :, ::-1]
 
 
102
 
103
  # === Download CSV ===
104
  def download_csv():
@@ -108,36 +150,57 @@ def download_csv():
108
 
109
  # === Reset all ===
110
  def reset_all():
111
- df.drop(df.index, inplace=True)
112
- for f in os.listdir(OUTPUT_DIR):
113
- os.remove(os.path.join(OUTPUT_DIR, f))
 
 
 
 
 
 
 
 
 
 
114
  if state["cap"]:
115
  state["cap"].release()
116
- state.update({"video_path": None, "cap": None, "frame": None, "play": False})
 
 
 
 
 
 
117
  return "πŸ” Reset done.", None
118
 
119
  # === UI ===
120
- with gr.Blocks() as app:
121
- gr.Markdown("## 🏹 Archery Pose Dataset Tool (Auto-Pause on Capture)")
 
122
 
123
  video_input = gr.Video(label="🎞️ Upload Video")
124
- load_btn = gr.Button("πŸ“‚ Load Video")
125
- status = gr.Textbox(label="Status")
126
 
127
  with gr.Row():
128
- play_btn = gr.Button("▢️ Play")
129
- pause_btn = gr.Button("⏸️ Pause")
130
- show_btn = gr.Button("πŸ–ΌοΈ Show Current Frame")
131
 
132
- image_output = gr.Image(label="Current Frame")
133
- caption_input = gr.Textbox(label="Caption")
134
- capture_btn = gr.Button("πŸ“Έ Capture & Pause")
 
 
135
 
136
  with gr.Row():
 
137
  download_btn = gr.Button("πŸ“₯ Download CSV")
138
- reset_btn = gr.Button("πŸ”„ Reset")
139
 
140
  csv_file = gr.File(label="πŸ“„ Dataset CSV")
 
141
 
142
  # Bind actions
143
  load_btn.click(load_video, inputs=video_input, outputs=status)
@@ -145,7 +208,12 @@ with gr.Blocks() as app:
145
  pause_btn.click(pause_video, outputs=status)
146
  show_btn.click(show_frame, outputs=image_output)
147
  capture_btn.click(capture_frame, inputs=caption_input, outputs=[status, image_output])
 
148
  download_btn.click(download_csv, outputs=csv_file)
149
  reset_btn.click(reset_all, outputs=[status, image_output])
 
 
 
150
 
151
- app.launch()
 
 
6
  import time
7
  import mediapipe as mp
8
  import pandas as pd
9
+ from concurrent.futures import ThreadPoolExecutor
10
+ import queue
11
 
12
  # === Setup ===
13
  OUTPUT_DIR = "captured_frames"
 
19
  state = {
20
  "cap": None,
21
  "frame": None,
22
+ "frame_rgb": None, # Pre-converted RGB frame
23
  "play": False,
24
+ "video_path": None,
25
+ "capture_queue": queue.Queue(),
26
+ "processing_thread": None
27
  }
28
 
29
+ # Thread pool for background processing
30
+ executor = ThreadPoolExecutor(max_workers=2)
31
+
32
+ # === Background pose processing ===
33
+ def process_pose_async(frame_bgr, filename, caption):
34
+ """Process pose estimation in background thread"""
35
+ try:
36
+ # Convert to RGB for MediaPipe
37
+ frame_rgb = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
38
+ results = pose.process(frame_rgb)
39
+
40
+ coords = []
41
+ if results.pose_landmarks:
42
+ for lm in results.pose_landmarks.landmark:
43
+ coords.append((round(lm.x, 5), round(lm.y, 5), round(lm.z, 5)))
44
+
45
+ # Add to dataframe
46
+ global df
47
+ new_row = pd.DataFrame([{
48
+ "filename": filename,
49
+ "caption": caption,
50
+ "pose_coords": coords
51
+ }])
52
+ df = pd.concat([df, new_row], ignore_index=True)
53
+
54
+ except Exception as e:
55
+ print(f"Error processing pose: {e}")
56
+
57
  # === Load Video ===
58
  def load_video(video_file):
59
  try:
 
67
  state["cap"].release()
68
 
69
  state["cap"] = cv2.VideoCapture(video_path)
70
+
71
+ # Set buffer size to reduce lag
72
+ state["cap"].set(cv2.CAP_PROP_BUFFERSIZE, 1)
73
+
74
  state["frame"] = None
75
+ state["frame_rgb"] = None
76
  state["play"] = False
77
  return "βœ… Video loaded successfully!"
78
  except Exception as e:
 
90
  if not ret:
91
  state["play"] = False
92
  break
93
+
94
+ # Store both BGR and RGB versions
95
  state["frame"] = frame
96
+ state["frame_rgb"] = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
97
+
98
+ # Adaptive delay based on video FPS
99
+ fps = state["cap"].get(cv2.CAP_PROP_FPS)
100
+ if fps > 0:
101
+ time.sleep(1.0 / fps)
102
+ else:
103
+ time.sleep(0.033) # ~30 FPS fallback
104
+
105
+ threading.Thread(target=stream, daemon=True).start()
106
  return "▢️ Playing..."
107
 
108
  # === Pause playback ===
 
112
 
113
  # === Show current frame ===
114
  def show_frame():
115
+ if state["frame_rgb"] is not None:
116
+ return state["frame_rgb"] # Already in RGB
117
  return None
118
 
119
+ # === Fast capture frame (immediate pause + async processing) ===
120
  def capture_frame(caption):
121
  if state["frame"] is None:
122
  return "⚠️ No frame to capture.", None
123
 
124
+ # IMMEDIATE pause - this is the key optimization
125
  state["play"] = False
126
+
127
+ # Capture current frame immediately
128
+ frame_bgr = state["frame"].copy() # Copy to avoid race conditions
129
+ frame_rgb = state["frame_rgb"].copy() if state["frame_rgb"] is not None else cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
130
+
131
+ # Generate filename and save immediately
132
  filename = f"{uuid.uuid4().hex[:8]}.jpg"
133
  path = os.path.join(OUTPUT_DIR, filename)
134
+ cv2.imwrite(path, frame_bgr)
135
+
136
+ # Process pose estimation in background (non-blocking)
137
+ executor.submit(process_pose_async, frame_bgr, filename, caption)
138
+
139
+ return f"βœ… Captured & paused: {filename} (processing pose...)", frame_rgb
 
 
 
 
 
 
 
 
 
 
 
140
 
141
+ # === Show dataset info ===
142
+ def show_dataset_info():
143
+ return f"πŸ“Š Dataset contains {len(df)} samples"
144
 
145
  # === Download CSV ===
146
  def download_csv():
 
150
 
151
  # === Reset all ===
152
  def reset_all():
153
+ global df
154
+ df = pd.DataFrame(columns=["filename", "caption", "pose_coords"])
155
+
156
+ # Clean up files
157
+ try:
158
+ for f in os.listdir(OUTPUT_DIR):
159
+ file_path = os.path.join(OUTPUT_DIR, f)
160
+ if os.path.isfile(file_path):
161
+ os.remove(file_path)
162
+ except Exception as e:
163
+ print(f"Error cleaning files: {e}")
164
+
165
+ # Reset video state
166
  if state["cap"]:
167
  state["cap"].release()
168
+ state.update({
169
+ "video_path": None,
170
+ "cap": None,
171
+ "frame": None,
172
+ "frame_rgb": None,
173
+ "play": False
174
+ })
175
  return "πŸ” Reset done.", None
176
 
177
  # === UI ===
178
+ with gr.Blocks(title="Fast Archery Pose Capture") as app:
179
+ gr.Markdown("## 🏹 Archery Pose Dataset Tool (Optimized for Speed)")
180
+ gr.Markdown("⚑ **Optimized**: Instant capture with background pose processing")
181
 
182
  video_input = gr.Video(label="🎞️ Upload Video")
183
+ load_btn = gr.Button("πŸ“‚ Load Video", variant="primary")
184
+ status = gr.Textbox(label="Status", interactive=False)
185
 
186
  with gr.Row():
187
+ play_btn = gr.Button("▢️ Play", variant="secondary")
188
+ pause_btn = gr.Button("⏸️ Pause", variant="secondary")
189
+ show_btn = gr.Button("πŸ–ΌοΈ Show Current Frame", variant="secondary")
190
 
191
+ image_output = gr.Image(label="Current Frame", height=400)
192
+
193
+ with gr.Row():
194
+ caption_input = gr.Textbox(label="Caption", placeholder="Describe the pose...")
195
+ capture_btn = gr.Button("πŸ“Έ Capture & Pause", variant="primary", size="lg")
196
 
197
  with gr.Row():
198
+ info_btn = gr.Button("πŸ“Š Dataset Info")
199
  download_btn = gr.Button("πŸ“₯ Download CSV")
200
+ reset_btn = gr.Button("πŸ”„ Reset", variant="stop")
201
 
202
  csv_file = gr.File(label="πŸ“„ Dataset CSV")
203
+ dataset_info = gr.Textbox(label="Dataset Info", interactive=False)
204
 
205
  # Bind actions
206
  load_btn.click(load_video, inputs=video_input, outputs=status)
 
208
  pause_btn.click(pause_video, outputs=status)
209
  show_btn.click(show_frame, outputs=image_output)
210
  capture_btn.click(capture_frame, inputs=caption_input, outputs=[status, image_output])
211
+ info_btn.click(show_dataset_info, outputs=dataset_info)
212
  download_btn.click(download_csv, outputs=csv_file)
213
  reset_btn.click(reset_all, outputs=[status, image_output])
214
+
215
+ # Auto-refresh frame display while playing
216
+ app.load(lambda: None) # Initialize
217
 
218
+ if __name__ == "__main__":
219
+ app.launch(share=False, server_name="0.0.0.0", server_port=7860)