Tremick commited on
Commit
ea87bea
·
verified ·
1 Parent(s): 5a4886c

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -299
app.py DELETED
@@ -1,299 +0,0 @@
1
- """
2
- Optical Flow Server for Hugging Face Spaces
3
- ============================================
4
- Complete server for cloud-based optical flow processing.
5
- Deploy this directly to Hugging Face Spaces.
6
- """
7
-
8
- import gradio as gr
9
- import cv2
10
- import numpy as np
11
- import base64
12
- from io import BytesIO
13
- from PIL import Image
14
- from sklearn.cluster import DBSCAN
15
- import colorsys
16
- from collections import deque
17
- import uuid
18
- import time
19
-
20
-
21
- class ServerOpticalFlowEngine:
22
- """Optical Flow Engine optimized for server-side processing."""
23
-
24
- def __init__(self):
25
- self.prev_gray = None
26
- self.trail_points = {}
27
- self.max_trail_length = 30
28
-
29
- self.bg_subtractor = cv2.createBackgroundSubtractorMOG2(
30
- history=500, varThreshold=16, detectShadows=True
31
- )
32
- self.color_pool = self._generate_colors(20)
33
-
34
- def _generate_colors(self, n):
35
- colors = []
36
- for i in range(n):
37
- hue = i / n
38
- rgb = colorsys.hsv_to_rgb(hue, 0.9, 0.95)
39
- bgr = (int(rgb[2] * 255), int(rgb[1] * 255), int(rgb[0] * 255))
40
- colors.append(bgr)
41
- return colors
42
-
43
- def compute_flow(self, gray):
44
- if self.prev_gray is None:
45
- self.prev_gray = gray.copy()
46
- return None
47
-
48
- flow = cv2.calcOpticalFlowFarneback(
49
- self.prev_gray, gray, None,
50
- pyr_scale=0.5, levels=5, winsize=13,
51
- iterations=10, poly_n=5, poly_sigma=1.1,
52
- flags=cv2.OPTFLOW_FARNEBACK_GAUSSIAN
53
- )
54
- self.prev_gray = gray.copy()
55
- return flow
56
-
57
- def segment_motion(self, frame):
58
- fg_mask = self.bg_subtractor.apply(frame)
59
- fg_mask[fg_mask == 127] = 0
60
-
61
- kernel_small = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
62
- fg_mask = cv2.erode(fg_mask, kernel_small, iterations=2)
63
-
64
- kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
65
- fg_mask = cv2.morphologyEx(fg_mask, cv2.MORPH_OPEN, kernel)
66
-
67
- kernel_large = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15))
68
- fg_mask = cv2.morphologyEx(fg_mask, cv2.MORPH_CLOSE, kernel_large)
69
-
70
- fg_mask = cv2.dilate(fg_mask, kernel_small, iterations=1)
71
- return fg_mask
72
-
73
- def detect_objects(self, mask, flow=None, min_area=2000):
74
- contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
75
-
76
- if not contours:
77
- return None, {}
78
-
79
- label_image = np.full(mask.shape, -1, dtype=np.int32)
80
- objects = {}
81
- label_id = 0
82
-
83
- for contour in contours:
84
- area = cv2.contourArea(contour)
85
- if area < min_area:
86
- continue
87
-
88
- x, y, w, h = cv2.boundingRect(contour)
89
- aspect_ratio = float(h) / w if w > 0 else 0
90
- if aspect_ratio < 0.2 or aspect_ratio > 8.0:
91
- continue
92
-
93
- hull = cv2.convexHull(contour)
94
- hull_area = cv2.contourArea(hull)
95
- if hull_area > 0 and (area / hull_area) < 0.3:
96
- continue
97
-
98
- cv2.drawContours(label_image, [contour], -1, label_id, -1)
99
-
100
- M = cv2.moments(contour)
101
- cx = int(M['m10'] / M['m00']) if M['m00'] > 0 else x + w // 2
102
- cy = int(M['m01'] / M['m00']) if M['m00'] > 0 else y + h // 2
103
-
104
- avg_velocity = (0, 0)
105
- if flow is not None:
106
- contour_mask = np.zeros(mask.shape, dtype=np.uint8)
107
- cv2.drawContours(contour_mask, [contour], -1, 255, -1)
108
- flow_x = flow[:, :, 0][contour_mask > 0]
109
- flow_y = flow[:, :, 1][contour_mask > 0]
110
- if len(flow_x) > 0:
111
- avg_velocity = (float(np.mean(flow_x)), float(np.mean(flow_y)))
112
-
113
- objects[label_id] = {
114
- 'centroid': (cx, cy),
115
- 'bbox': (x, y, x + w, y + h),
116
- 'velocity': avg_velocity,
117
- 'area': area,
118
- 'contour': contour
119
- }
120
- label_id += 1
121
-
122
- return label_image, objects if objects else (None, {})
123
-
124
- def update_trails(self, objects):
125
- current_ids = set(objects.keys())
126
- for old_id in set(self.trail_points.keys()) - current_ids:
127
- del self.trail_points[old_id]
128
-
129
- for obj_id, obj in objects.items():
130
- if obj_id not in self.trail_points:
131
- self.trail_points[obj_id] = deque(maxlen=self.max_trail_length)
132
- self.trail_points[obj_id].append(obj['centroid'])
133
-
134
- def draw_results(self, frame, objects, label_image):
135
- output = frame.copy()
136
-
137
- for label, obj in objects.items():
138
- color = self.color_pool[label % len(self.color_pool)]
139
- x1, y1, x2, y2 = obj['bbox']
140
- cv2.rectangle(output, (x1, y1), (x2, y2), color, 2)
141
-
142
- cx, cy = obj['centroid']
143
- cv2.circle(output, (cx, cy), 5, color, -1)
144
-
145
- vx, vy = obj['velocity']
146
- if np.sqrt(vx**2 + vy**2) > 1:
147
- cv2.arrowedLine(output, (cx, cy),
148
- (int(cx + vx * 5), int(cy + vy * 5)),
149
- (0, 255, 255), 3, tipLength=0.3)
150
-
151
- cv2.putText(output, f"Obj {label+1}", (x1, y1 - 25),
152
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
153
- cv2.putText(output, f"Speed: {np.sqrt(vx**2 + vy**2):.1f}",
154
- (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)
155
-
156
- if label_image is not None:
157
- mask = (label_image == label).astype(np.uint8) * 255
158
- contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
159
- cv2.drawContours(output, contours, -1, color, 2)
160
-
161
- return output
162
-
163
- def draw_trails(self, frame):
164
- output = frame.copy()
165
- for obj_id, trail in self.trail_points.items():
166
- if len(trail) < 2:
167
- continue
168
- color = self.color_pool[obj_id % len(self.color_pool)]
169
- points = np.array(list(trail), dtype=np.int32)
170
- for i in range(1, len(points)):
171
- thickness = int(1 + (i / len(points)) * 3)
172
- cv2.line(output, tuple(points[i-1]), tuple(points[i]), color, thickness)
173
- cv2.circle(output, tuple(points[-1]), 6, color, -1)
174
- return output
175
-
176
- def compute_heatmap(self, flow):
177
- if flow is None:
178
- return None
179
- magnitude = np.sqrt(flow[..., 0]**2 + flow[..., 1]**2)
180
- normalized = cv2.normalize(magnitude, None, 0, 255, cv2.NORM_MINMAX)
181
- return cv2.applyColorMap(normalized.astype(np.uint8), cv2.COLORMAP_HOT)
182
-
183
- def process_frame(self, frame):
184
- gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
185
- flow = self.compute_flow(gray)
186
- motion_mask = self.segment_motion(frame)
187
-
188
- results = {
189
- 'num_objects': 0,
190
- 'tracked': frame.copy(),
191
- 'trails': frame.copy(),
192
- 'heatmap': np.zeros_like(frame)
193
- }
194
-
195
- if flow is not None:
196
- results['heatmap'] = self.compute_heatmap(flow)
197
- label_image, objects = self.detect_objects(motion_mask, flow)
198
-
199
- if objects:
200
- results['num_objects'] = len(objects)
201
- self.update_trails(objects)
202
- results['tracked'] = self.draw_results(frame, objects, label_image)
203
- results['trails'] = self.draw_trails(results['tracked'])
204
-
205
- return results
206
-
207
- def reset(self):
208
- self.prev_gray = None
209
- self.trail_points = {}
210
-
211
-
212
- # Session management
213
- sessions = {}
214
- SESSION_TIMEOUT = 300
215
-
216
-
217
- def get_or_create_session(session_id):
218
- current_time = time.time()
219
- expired = [sid for sid, data in sessions.items()
220
- if current_time - data['last_access'] > SESSION_TIMEOUT]
221
- for sid in expired:
222
- del sessions[sid]
223
-
224
- if session_id not in sessions:
225
- sessions[session_id] = {
226
- 'engine': ServerOpticalFlowEngine(),
227
- 'last_access': current_time
228
- }
229
- else:
230
- sessions[session_id]['last_access'] = current_time
231
-
232
- return sessions[session_id]['engine']
233
-
234
-
235
- def decode_frame(base64_data):
236
- img_data = base64.b64decode(base64_data)
237
- img = Image.open(BytesIO(img_data))
238
- return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
239
-
240
-
241
- def encode_frame(frame):
242
- _, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 85])
243
- return base64.b64encode(buffer).decode('utf-8')
244
-
245
-
246
- def process_frame_api(frame_base64, session_id):
247
- """Process a single frame."""
248
- try:
249
- engine = get_or_create_session(session_id)
250
- frame = decode_frame(frame_base64)
251
- results = engine.process_frame(frame)
252
-
253
- return {
254
- 'success': True,
255
- 'num_objects': results['num_objects'],
256
- 'tracked': encode_frame(results['tracked']),
257
- 'trails': encode_frame(results['trails']),
258
- 'heatmap': encode_frame(results['heatmap'])
259
- }
260
- except Exception as e:
261
- return {'success': False, 'error': str(e)}
262
-
263
-
264
- def reset_session_api(session_id):
265
- """Reset session state."""
266
- if session_id in sessions:
267
- sessions[session_id]['engine'].reset()
268
- return {'success': True}
269
-
270
-
271
- def create_new_session():
272
- """Create new session."""
273
- session_id = str(uuid.uuid4())
274
- get_or_create_session(session_id)
275
- return session_id
276
-
277
-
278
- # Gradio interface
279
- with gr.Blocks(title="Optical Flow Server") as demo:
280
- gr.Markdown("# 🎥 Optical Flow Processing Server")
281
- gr.Markdown("Use with: `python optical_flow_advanced.py --server <this-url>`")
282
-
283
- with gr.Tab("Test"):
284
- with gr.Row():
285
- input_image = gr.Image(label="Input Frame", type="numpy")
286
- output_image = gr.Image(label="Processed")
287
-
288
- def test_process(image):
289
- if image is None:
290
- return None
291
- engine = ServerOpticalFlowEngine()
292
- engine.process_frame(image)
293
- return engine.process_frame(image)['tracked']
294
-
295
- input_image.change(test_process, inputs=input_image, outputs=output_image)
296
-
297
-
298
- if __name__ == "__main__":
299
- demo.launch(server_name="0.0.0.0", server_port=7860)