Commit
·
57ca54b
1
Parent(s):
55219fd
commented the logs
Browse files
backend/gradio_labanmovementanalysis/visualizer.py
CHANGED
|
@@ -68,7 +68,7 @@ class PoseVisualizer:
|
|
| 68 |
show_direction_arrows: Whether to show movement direction arrows
|
| 69 |
show_metrics: Whether to display text metrics on frame
|
| 70 |
"""
|
| 71 |
-
print("[TRACE] PoseVisualizer.__init__ called")
|
| 72 |
self.trail_length = trail_length
|
| 73 |
self.show_skeleton = show_skeleton
|
| 74 |
self.show_trails = show_trails
|
|
@@ -102,16 +102,16 @@ class PoseVisualizer:
|
|
| 102 |
Returns:
|
| 103 |
Annotated frame
|
| 104 |
"""
|
| 105 |
-
print(f"[TRACE] visualize_frame(frame_index={frame_index}, num_poses={len(pose_results)})")
|
| 106 |
vis_frame = frame.copy()
|
| 107 |
if not pose_results:
|
| 108 |
-
print(f"[DEBUG] Frame {frame_index}: No pose results (no detections)")
|
| 109 |
return vis_frame # No people detected, return original frame
|
| 110 |
|
| 111 |
# Draw for each detected person
|
| 112 |
for person_idx, pose in enumerate(pose_results):
|
| 113 |
if not pose.keypoints or all(math.isnan(kp.x) or math.isnan(kp.y) for kp in pose.keypoints):
|
| 114 |
-
print(f"[DEBUG] Frame {frame_index}, Person {person_idx}: All keypoints are NaN or missing, skipping drawing for this person.")
|
| 115 |
continue
|
| 116 |
|
| 117 |
# Update trails
|
|
@@ -156,7 +156,7 @@ class PoseVisualizer:
|
|
| 156 |
Returns:
|
| 157 |
Path to created video
|
| 158 |
"""
|
| 159 |
-
print(f"[TRACE] generate_overlay_video(num_frames={len(frames)})")
|
| 160 |
try:
|
| 161 |
if len(frames) != len(all_pose_results) or len(frames) != len(all_movement_metrics):
|
| 162 |
raise ValueError("Mismatched lengths between frames, poses, and metrics")
|
|
@@ -167,9 +167,9 @@ class PoseVisualizer:
|
|
| 167 |
):
|
| 168 |
annotated_frame = self.visualize_frame(frame, poses, metrics, i)
|
| 169 |
annotated_frames.append(annotated_frame)
|
| 170 |
-
print(f"[DEBUG] About to assemble video with {len(annotated_frames)} frames.")
|
| 171 |
for idx, f in enumerate(annotated_frames):
|
| 172 |
-
print(f"[DEBUG] Frame {idx} shape: {f.shape if hasattr(f, 'shape') else type(f)}")
|
| 173 |
from . import video_utils
|
| 174 |
return video_utils.assemble_video(annotated_frames, output_path, fps)
|
| 175 |
except Exception as e:
|
|
@@ -179,7 +179,7 @@ class PoseVisualizer:
|
|
| 179 |
|
| 180 |
def _update_trails(self, pose: PoseResult, person_id: int):
|
| 181 |
"""Update motion trails for a person."""
|
| 182 |
-
print(f"[TRACE] _update_trails(person_id={person_id})")
|
| 183 |
if person_id not in self.trails:
|
| 184 |
self.trails[person_id] = {}
|
| 185 |
|
|
@@ -196,7 +196,7 @@ class PoseVisualizer:
|
|
| 196 |
|
| 197 |
def _draw_trails(self, frame: np.ndarray, person_id: int):
|
| 198 |
"""Draw motion trails for a person."""
|
| 199 |
-
print(f"[TRACE] _draw_trails(person_id={person_id})")
|
| 200 |
if person_id not in self.trails:
|
| 201 |
return
|
| 202 |
|
|
@@ -211,9 +211,9 @@ class PoseVisualizer:
|
|
| 211 |
x1, y1 = trail[i-1]
|
| 212 |
x2, y2 = trail[i]
|
| 213 |
if math.isnan(x1) or math.isnan(y1) or math.isnan(x2) or math.isnan(y2):
|
| 214 |
-
print(f"[DEBUG] _draw_trails: NaN detected in trail for person {person_id}, joint {joint_name}, skipping segment.")
|
| 215 |
continue
|
| 216 |
-
print(f"[DEBUG] _draw_trails: Converting to int: ({x1}*{w}, {y1}*{h}) and ({x2}*{w}, {y2}*{h})")
|
| 217 |
alpha = i / len(trail)
|
| 218 |
color = tuple(int(c * alpha) for c in (255, 255, 255))
|
| 219 |
|
|
@@ -226,7 +226,7 @@ class PoseVisualizer:
|
|
| 226 |
|
| 227 |
def _draw_skeleton(self, frame: np.ndarray, pose: PoseResult, color: Tuple[int, int, int]):
|
| 228 |
"""Draw pose skeleton."""
|
| 229 |
-
print(f"[TRACE] _draw_skeleton()")
|
| 230 |
h, w = frame.shape[:2]
|
| 231 |
|
| 232 |
# Create keypoint lookup
|
|
@@ -251,10 +251,10 @@ class PoseVisualizer:
|
|
| 251 |
kp2 = kp_dict[name2]
|
| 252 |
|
| 253 |
if math.isnan(kp1.x) or math.isnan(kp1.y) or math.isnan(kp2.x) or math.isnan(kp2.y):
|
| 254 |
-
print(f"[DEBUG] _draw_skeleton: NaN detected in skeleton for keypoints {name1}, {name2}, skipping line.")
|
| 255 |
continue
|
| 256 |
|
| 257 |
-
print(f"[DEBUG] _draw_skeleton: Converting to int: ({kp1.x}*{w}, {kp1.y}*{h}) and ({kp2.x}*{w}, {kp2.y}*{h})")
|
| 258 |
# Convert to pixel coordinates
|
| 259 |
pt1 = (int(kp1.x * w), int(kp1.y * h))
|
| 260 |
pt2 = (int(kp2.x * w), int(kp2.y * h))
|
|
@@ -265,7 +265,7 @@ class PoseVisualizer:
|
|
| 265 |
def _draw_keypoints(self, frame: np.ndarray, pose: PoseResult,
|
| 266 |
metrics: Optional[MovementMetrics] = None):
|
| 267 |
"""Draw individual keypoints."""
|
| 268 |
-
print(f"[TRACE] _draw_keypoints()")
|
| 269 |
h, w = frame.shape[:2]
|
| 270 |
|
| 271 |
for kp in pose.keypoints:
|
|
@@ -273,10 +273,10 @@ class PoseVisualizer:
|
|
| 273 |
continue
|
| 274 |
|
| 275 |
if math.isnan(kp.x) or math.isnan(kp.y):
|
| 276 |
-
print(f"[DEBUG] _draw_keypoints: NaN detected in keypoint {kp.name}, skipping.")
|
| 277 |
continue
|
| 278 |
|
| 279 |
-
print(f"[DEBUG] _draw_keypoints: Converting to int: ({kp.x}*{w}, {kp.y}*{h})")
|
| 280 |
# Convert to pixel coordinates
|
| 281 |
pt = (int(kp.x * w), int(kp.y * h))
|
| 282 |
|
|
@@ -290,7 +290,7 @@ class PoseVisualizer:
|
|
| 290 |
def _draw_direction_arrow(self, frame: np.ndarray, pose: PoseResult,
|
| 291 |
metrics: MovementMetrics):
|
| 292 |
"""Draw arrow indicating movement direction."""
|
| 293 |
-
print(f"[TRACE] _draw_direction_arrow()")
|
| 294 |
if metrics.direction == Direction.STATIONARY:
|
| 295 |
return
|
| 296 |
|
|
@@ -300,13 +300,13 @@ class PoseVisualizer:
|
|
| 300 |
xs = [kp.x for kp in pose.keypoints if kp.confidence > 0.3 and not math.isnan(kp.x)]
|
| 301 |
ys = [kp.y for kp in pose.keypoints if kp.confidence > 0.3 and not math.isnan(kp.y)]
|
| 302 |
if not xs or not ys:
|
| 303 |
-
print(f"[DEBUG] _draw_direction_arrow: No valid keypoints for direction arrow, skipping.")
|
| 304 |
return
|
| 305 |
-
print(f"[DEBUG] _draw_direction_arrow: Converting to int: ({np.mean(xs)}*{w}, {np.mean(ys)}*{h})")
|
| 306 |
center_x = np.mean(xs)
|
| 307 |
center_y = np.mean(ys)
|
| 308 |
if math.isnan(center_x) or math.isnan(center_y):
|
| 309 |
-
print(f"[DEBUG] _draw_direction_arrow: NaN detected in center, skipping arrow.")
|
| 310 |
return
|
| 311 |
center = (int(center_x * w), int(center_y * h))
|
| 312 |
|
|
@@ -334,7 +334,7 @@ class PoseVisualizer:
|
|
| 334 |
|
| 335 |
def _draw_metrics_overlay(self, frame: np.ndarray, metrics: MovementMetrics):
|
| 336 |
"""Draw text overlay with movement metrics."""
|
| 337 |
-
print(f"[TRACE] _draw_metrics_overlay()")
|
| 338 |
# Define text properties
|
| 339 |
font = cv2.FONT_HERSHEY_SIMPLEX
|
| 340 |
font_scale = 0.6
|
|
@@ -343,7 +343,7 @@ class PoseVisualizer:
|
|
| 343 |
# Sanitize metrics
|
| 344 |
def safe_metric(val, name):
|
| 345 |
if isinstance(val, float) and math.isnan(val):
|
| 346 |
-
print(f"[DEBUG] _draw_metrics_overlay: {name} is NaN, replacing with 0.0")
|
| 347 |
return 0.0
|
| 348 |
return val
|
| 349 |
|
|
@@ -381,7 +381,7 @@ class PoseVisualizer:
|
|
| 381 |
|
| 382 |
def _get_color_for_metrics(self, metrics: Optional[MovementMetrics]) -> Tuple[int, int, int]:
|
| 383 |
"""Get color based on movement metrics."""
|
| 384 |
-
print(f"[TRACE] _get_color_for_metrics()")
|
| 385 |
if metrics is None:
|
| 386 |
return (255, 255, 255) # White default
|
| 387 |
|
|
@@ -389,7 +389,7 @@ class PoseVisualizer:
|
|
| 389 |
|
| 390 |
def _confidence_to_color(self, confidence: float) -> Tuple[int, int, int]:
|
| 391 |
"""Convert confidence score to color (green=high, red=low)."""
|
| 392 |
-
print(f"[TRACE] _confidence_to_color(confidence={confidence})")
|
| 393 |
# Use HSV color space for smooth gradient
|
| 394 |
hue = confidence * 120 # 0=red, 120=green
|
| 395 |
rgb = colorsys.hsv_to_rgb(hue / 360, 1.0, 1.0)
|
|
@@ -397,7 +397,7 @@ class PoseVisualizer:
|
|
| 397 |
|
| 398 |
def _get_skeleton_for_model(self, keypoints: List[Keypoint]) -> List[Tuple[int, int]]:
|
| 399 |
"""Determine which skeleton definition to use based on keypoints."""
|
| 400 |
-
print(f"[TRACE] _get_skeleton_for_model(num_keypoints={len(keypoints)})")
|
| 401 |
# Simple heuristic: if we have more than 20 keypoints, use MediaPipe skeleton
|
| 402 |
if len(keypoints) > 20:
|
| 403 |
return self.MEDIAPIPE_SKELETON
|
|
@@ -405,7 +405,7 @@ class PoseVisualizer:
|
|
| 405 |
|
| 406 |
def _get_keypoint_names_for_model(self, keypoints: List[Keypoint]) -> List[str]:
|
| 407 |
"""Get ordered list of keypoint names for the model."""
|
| 408 |
-
print(f"[TRACE] _get_keypoint_names_for_model(num_keypoints={len(keypoints)})")
|
| 409 |
# If keypoints have names, use them
|
| 410 |
if keypoints and keypoints[0].name:
|
| 411 |
return [kp.name for kp in keypoints]
|
|
|
|
| 68 |
show_direction_arrows: Whether to show movement direction arrows
|
| 69 |
show_metrics: Whether to display text metrics on frame
|
| 70 |
"""
|
| 71 |
+
# print("[TRACE] PoseVisualizer.__init__ called")
|
| 72 |
self.trail_length = trail_length
|
| 73 |
self.show_skeleton = show_skeleton
|
| 74 |
self.show_trails = show_trails
|
|
|
|
| 102 |
Returns:
|
| 103 |
Annotated frame
|
| 104 |
"""
|
| 105 |
+
# print(f"[TRACE] visualize_frame(frame_index={frame_index}, num_poses={len(pose_results)})")
|
| 106 |
vis_frame = frame.copy()
|
| 107 |
if not pose_results:
|
| 108 |
+
# print(f"[DEBUG] Frame {frame_index}: No pose results (no detections)")
|
| 109 |
return vis_frame # No people detected, return original frame
|
| 110 |
|
| 111 |
# Draw for each detected person
|
| 112 |
for person_idx, pose in enumerate(pose_results):
|
| 113 |
if not pose.keypoints or all(math.isnan(kp.x) or math.isnan(kp.y) for kp in pose.keypoints):
|
| 114 |
+
# print(f"[DEBUG] Frame {frame_index}, Person {person_idx}: All keypoints are NaN or missing, skipping drawing for this person.")
|
| 115 |
continue
|
| 116 |
|
| 117 |
# Update trails
|
|
|
|
| 156 |
Returns:
|
| 157 |
Path to created video
|
| 158 |
"""
|
| 159 |
+
# print(f"[TRACE] generate_overlay_video(num_frames={len(frames)})")
|
| 160 |
try:
|
| 161 |
if len(frames) != len(all_pose_results) or len(frames) != len(all_movement_metrics):
|
| 162 |
raise ValueError("Mismatched lengths between frames, poses, and metrics")
|
|
|
|
| 167 |
):
|
| 168 |
annotated_frame = self.visualize_frame(frame, poses, metrics, i)
|
| 169 |
annotated_frames.append(annotated_frame)
|
| 170 |
+
# print(f"[DEBUG] About to assemble video with {len(annotated_frames)} frames.")
|
| 171 |
for idx, f in enumerate(annotated_frames):
|
| 172 |
+
# print(f"[DEBUG] Frame {idx} shape: {f.shape if hasattr(f, 'shape') else type(f)}")
|
| 173 |
from . import video_utils
|
| 174 |
return video_utils.assemble_video(annotated_frames, output_path, fps)
|
| 175 |
except Exception as e:
|
|
|
|
| 179 |
|
| 180 |
def _update_trails(self, pose: PoseResult, person_id: int):
|
| 181 |
"""Update motion trails for a person."""
|
| 182 |
+
# print(f"[TRACE] _update_trails(person_id={person_id})")
|
| 183 |
if person_id not in self.trails:
|
| 184 |
self.trails[person_id] = {}
|
| 185 |
|
|
|
|
| 196 |
|
| 197 |
def _draw_trails(self, frame: np.ndarray, person_id: int):
|
| 198 |
"""Draw motion trails for a person."""
|
| 199 |
+
# print(f"[TRACE] _draw_trails(person_id={person_id})")
|
| 200 |
if person_id not in self.trails:
|
| 201 |
return
|
| 202 |
|
|
|
|
| 211 |
x1, y1 = trail[i-1]
|
| 212 |
x2, y2 = trail[i]
|
| 213 |
if math.isnan(x1) or math.isnan(y1) or math.isnan(x2) or math.isnan(y2):
|
| 214 |
+
# print(f"[DEBUG] _draw_trails: NaN detected in trail for person {person_id}, joint {joint_name}, skipping segment.")
|
| 215 |
continue
|
| 216 |
+
# print(f"[DEBUG] _draw_trails: Converting to int: ({x1}*{w}, {y1}*{h}) and ({x2}*{w}, {y2}*{h})")
|
| 217 |
alpha = i / len(trail)
|
| 218 |
color = tuple(int(c * alpha) for c in (255, 255, 255))
|
| 219 |
|
|
|
|
| 226 |
|
| 227 |
def _draw_skeleton(self, frame: np.ndarray, pose: PoseResult, color: Tuple[int, int, int]):
|
| 228 |
"""Draw pose skeleton."""
|
| 229 |
+
# print(f"[TRACE] _draw_skeleton()")
|
| 230 |
h, w = frame.shape[:2]
|
| 231 |
|
| 232 |
# Create keypoint lookup
|
|
|
|
| 251 |
kp2 = kp_dict[name2]
|
| 252 |
|
| 253 |
if math.isnan(kp1.x) or math.isnan(kp1.y) or math.isnan(kp2.x) or math.isnan(kp2.y):
|
| 254 |
+
# print(f"[DEBUG] _draw_skeleton: NaN detected in skeleton for keypoints {name1}, {name2}, skipping line.")
|
| 255 |
continue
|
| 256 |
|
| 257 |
+
# print(f"[DEBUG] _draw_skeleton: Converting to int: ({kp1.x}*{w}, {kp1.y}*{h}) and ({kp2.x}*{w}, {kp2.y}*{h})")
|
| 258 |
# Convert to pixel coordinates
|
| 259 |
pt1 = (int(kp1.x * w), int(kp1.y * h))
|
| 260 |
pt2 = (int(kp2.x * w), int(kp2.y * h))
|
|
|
|
| 265 |
def _draw_keypoints(self, frame: np.ndarray, pose: PoseResult,
|
| 266 |
metrics: Optional[MovementMetrics] = None):
|
| 267 |
"""Draw individual keypoints."""
|
| 268 |
+
# print(f"[TRACE] _draw_keypoints()")
|
| 269 |
h, w = frame.shape[:2]
|
| 270 |
|
| 271 |
for kp in pose.keypoints:
|
|
|
|
| 273 |
continue
|
| 274 |
|
| 275 |
if math.isnan(kp.x) or math.isnan(kp.y):
|
| 276 |
+
# print(f"[DEBUG] _draw_keypoints: NaN detected in keypoint {kp.name}, skipping.")
|
| 277 |
continue
|
| 278 |
|
| 279 |
+
# print(f"[DEBUG] _draw_keypoints: Converting to int: ({kp.x}*{w}, {kp.y}*{h})")
|
| 280 |
# Convert to pixel coordinates
|
| 281 |
pt = (int(kp.x * w), int(kp.y * h))
|
| 282 |
|
|
|
|
| 290 |
def _draw_direction_arrow(self, frame: np.ndarray, pose: PoseResult,
|
| 291 |
metrics: MovementMetrics):
|
| 292 |
"""Draw arrow indicating movement direction."""
|
| 293 |
+
# print(f"[TRACE] _draw_direction_arrow()")
|
| 294 |
if metrics.direction == Direction.STATIONARY:
|
| 295 |
return
|
| 296 |
|
|
|
|
| 300 |
xs = [kp.x for kp in pose.keypoints if kp.confidence > 0.3 and not math.isnan(kp.x)]
|
| 301 |
ys = [kp.y for kp in pose.keypoints if kp.confidence > 0.3 and not math.isnan(kp.y)]
|
| 302 |
if not xs or not ys:
|
| 303 |
+
# print(f"[DEBUG] _draw_direction_arrow: No valid keypoints for direction arrow, skipping.")
|
| 304 |
return
|
| 305 |
+
# print(f"[DEBUG] _draw_direction_arrow: Converting to int: ({np.mean(xs)}*{w}, {np.mean(ys)}*{h})")
|
| 306 |
center_x = np.mean(xs)
|
| 307 |
center_y = np.mean(ys)
|
| 308 |
if math.isnan(center_x) or math.isnan(center_y):
|
| 309 |
+
# print(f"[DEBUG] _draw_direction_arrow: NaN detected in center, skipping arrow.")
|
| 310 |
return
|
| 311 |
center = (int(center_x * w), int(center_y * h))
|
| 312 |
|
|
|
|
| 334 |
|
| 335 |
def _draw_metrics_overlay(self, frame: np.ndarray, metrics: MovementMetrics):
|
| 336 |
"""Draw text overlay with movement metrics."""
|
| 337 |
+
# print(f"[TRACE] _draw_metrics_overlay()")
|
| 338 |
# Define text properties
|
| 339 |
font = cv2.FONT_HERSHEY_SIMPLEX
|
| 340 |
font_scale = 0.6
|
|
|
|
| 343 |
# Sanitize metrics
|
| 344 |
def safe_metric(val, name):
|
| 345 |
if isinstance(val, float) and math.isnan(val):
|
| 346 |
+
# print(f"[DEBUG] _draw_metrics_overlay: {name} is NaN, replacing with 0.0")
|
| 347 |
return 0.0
|
| 348 |
return val
|
| 349 |
|
|
|
|
| 381 |
|
| 382 |
def _get_color_for_metrics(self, metrics: Optional[MovementMetrics]) -> Tuple[int, int, int]:
|
| 383 |
"""Get color based on movement metrics."""
|
| 384 |
+
# print(f"[TRACE] _get_color_for_metrics()")
|
| 385 |
if metrics is None:
|
| 386 |
return (255, 255, 255) # White default
|
| 387 |
|
|
|
|
| 389 |
|
| 390 |
def _confidence_to_color(self, confidence: float) -> Tuple[int, int, int]:
|
| 391 |
"""Convert confidence score to color (green=high, red=low)."""
|
| 392 |
+
# print(f"[TRACE] _confidence_to_color(confidence={confidence})")
|
| 393 |
# Use HSV color space for smooth gradient
|
| 394 |
hue = confidence * 120 # 0=red, 120=green
|
| 395 |
rgb = colorsys.hsv_to_rgb(hue / 360, 1.0, 1.0)
|
|
|
|
| 397 |
|
| 398 |
def _get_skeleton_for_model(self, keypoints: List[Keypoint]) -> List[Tuple[int, int]]:
|
| 399 |
"""Determine which skeleton definition to use based on keypoints."""
|
| 400 |
+
# print(f"[TRACE] _get_skeleton_for_model(num_keypoints={len(keypoints)})")
|
| 401 |
# Simple heuristic: if we have more than 20 keypoints, use MediaPipe skeleton
|
| 402 |
if len(keypoints) > 20:
|
| 403 |
return self.MEDIAPIPE_SKELETON
|
|
|
|
| 405 |
|
| 406 |
def _get_keypoint_names_for_model(self, keypoints: List[Keypoint]) -> List[str]:
|
| 407 |
"""Get ordered list of keypoint names for the model."""
|
| 408 |
+
# print(f"[TRACE] _get_keypoint_names_for_model(num_keypoints={len(keypoints)})")
|
| 409 |
# If keypoints have names, use them
|
| 410 |
if keypoints and keypoints[0].name:
|
| 411 |
return [kp.name for kp in keypoints]
|