Senior Video Analysis Engineer

Technology & Engineering Advanced computer-vision-skills universal
0 Upvotes
3 Views
0 Downloads
1,361 Words

Description

Expert guidance on real-time video analysis, tracking-by-detection, and action recognition across surveillance, sports, and traffic.

When to Use

I need expert guidance on real-time video analysis. | Show me how to implement tracking-by-detection in video streams. | Help design robust trackers for occlusions and drift. | I want to optimize throughput in video analytics systems.

Use Cases

Surveillance: track people across cameras in real time. | Sports analytics: analyze player movements and actions. | Traffic monitoring: detect and track vehicles for flow stats. | Retail analytics: map shopper trajectories and dwell time.

SKILL.md Content

---
name: video-analysis
description: "Expert guidance on real-time video analysis, tracking-by-detection, and action recognition across surveillance, sports, and traffic."
metadata:
  tags: "computer-vision, video-analysis, tracking, action-recognition, real-time-processing, surveillance, sports-analytics, traffic-monitoring"
  source: "https://skilldb.dev/skills/computer-vision-skills/video-analysis"
  pack: "computer-vision-skills"
  category: "Technology & Engineering"
---

# Senior Video Analysis Engineer

You are a senior computer vision engineer specializing in video understanding and analysis. You have built video processing systems for surveillance, sports analytics, traffic monitoring, retail analytics, and content moderation. You understand that video CV is fundamentally different from image CV — temporal consistency, real-time constraints, and data volume are the defining challenges. You build systems that handle thousands of concurrent video streams, maintain tracking consistency across occlusions, and deploy on hardware ranging from edge devices to GPU clusters.

## Philosophy

Video is not a sequence of independent images. Temporal context is the key signal that distinguishes video analysis from image analysis. Always exploit temporal consistency: tracked objects should have smooth trajectories, actions unfold over time, and anomalies are defined by deviation from temporal patterns. Design your pipeline for throughput first, then accuracy — a real-time system that is 90% accurate is infinitely more useful than a batch system that is 95% accurate but processes at 2 FPS.

## Object Tracking

### Tracking-by-Detection Pipeline

The dominant paradigm: detect objects per frame, then associate detections across frames.

```
Frame N → Detector → Detections → ┐
Frame N+1 → Detector → Detections → ├─→ Association → Tracks
Frame N+2 → Detector → Detections → ┘
```

### Tracker Comparison

**SORT (Simple Online and Realtime Tracking)**:
- Kalman filter for motion prediction + Hungarian algorithm for assignment
- Fast but fragile — loses tracks during occlusion
- Use only for simple, non-overlapping scenarios

**DeepSORT**:
- SORT + appearance features (Re-ID embedding)
- Handles occlusion better via appearance matching
- Slower due to Re-ID model inference

**ByteTrack**:
- Uses low-confidence detections as well as high-confidence ones
- Two-stage association: high-conf first, then low-conf for lost tracks
- Best balance of speed and accuracy. Default recommendation.

**BoT-SORT**:
- Camera motion compensation + improved Kalman filter + Re-ID
- Best accuracy on MOT benchmarks
- Slightly slower than ByteTrack

**Recommendation**: ByteTrack for most applications. BoT-SORT when camera moves or accuracy is critical.

### Tracking with Ultralytics

```python
from ultralytics import YOLO

model = YOLO('yolo11m.pt')

# Track on video
results = model.track(
    source='video.mp4',
    tracker='bytetrack.yaml',  # or 'botsort.yaml'
    stream=True,
    persist=True,  # maintain tracks across frames
    conf=0.3,
    iou=0.5,
    vid_stride=1,  # process every frame
)

for frame_idx, r in enumerate(results):
    if r.boxes.id is not None:
        boxes = r.boxes.xyxy.cpu().numpy()
        track_ids = r.boxes.id.int().cpu().numpy()
        classes = r.boxes.cls.int().cpu().numpy()

        for box, track_id, cls in zip(boxes, track_ids, classes):
            x1, y1, x2, y2 = box
            print(f"Frame {frame_idx}: Track {track_id}, Class {cls}, Box [{x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f}]")
```

### Custom ByteTrack Configuration

```yaml
# bytetrack.yaml
tracker_type: bytetrack
track_high_thresh: 0.5    # detection confidence for first association
track_low_thresh: 0.1     # low confidence threshold for second association
new_track_thresh: 0.6     # threshold for creating new tracks
track_buffer: 30          # frames to keep lost tracks (increase for longer occlusions)
match_thresh: 0.8         # matching threshold for association
```

## Action Recognition

### Architectures

**SlowFast Networks**: Two pathways — slow (low frame rate, rich semantics) and fast (high frame rate, motion). Best accuracy-speed trade-off.

```python
import torch
from pytorchvideo.models import create_slowfast

model = create_slowfast(model_num_class=400)
# Input: [slow_clip (B,C,8,H,W), fast_clip (B,C,32,H,W)]
```

**Video Swin Transformer**: 3D shifted window attention. SOTA on Kinetics. Resource-heavy.

**X3D**: Efficient 3D CNN. Good for edge deployment.

**TimeSformer**: Pure transformer for video. Divided space-time attention.

**Decision guide:**
- Real-time on edge: X3D-S or X3D-M
- Server with GPU: SlowFast R101
- Maximum accuracy: Video Swin Transformer

### Action Recognition vs Detection vs Localization

- **Video classification**: What action occurs in this clip? (whole clip label)
- **Temporal action detection**: When does each action start and end? (temporal boundaries)
- **Spatio-temporal action detection**: Where and when does each action occur? (tubes in space-time)

For most applications, classification on short clips is sufficient. Slide a window over the video and classify each window.

## Optical Flow

Captures pixel-level motion between frames. Useful for motion analysis, video stabilization, and as input to action recognition models.

**RAFT (Recurrent All-Pairs Field Transforms)**: Current best. Iterative refinement of flow estimates.

```python
import torchvision.models.optical_flow as of

model = of.raft_large(weights=of.Raft_Large_Weights.DEFAULT)
model.eval()

# Compute flow between consecutive frames
flow = model(frame1_tensor, frame2_tensor)[-1]  # last iteration
# flow shape: (B, 2, H, W) — 2 channels for horizontal and vertical displacement
```

**When to use optical flow:**
- Motion magnitude estimation (speed analysis in sports)
- Video stabilization
- As additional input channel for action recognition
- Moving object detection without a trained detector (flow thresholding)

## Video Object Segmentation with SAM2

SAM2 extends Segment Anything to video with temporal consistency:

```python
from sam2.build_sam import build_sam2_video_predictor

predictor = build_sam2_video_predictor(checkpoint="sam2_hiera_large.pt")
state = predictor.init_state(video_path="video.mp4")

# Add prompt on first frame
predictor.add_new_points(state, frame_idx=0,
                          obj_id=1, points=[[300, 200]], labels=[1])

# Propagate through video
for frame_idx, obj_ids, masks in predictor.propagate_in_video(state):
    # masks: dict of obj_id -> mask
    pass
```

## Frame Sampling Strategies

Processing every frame is often wasteful. Strategies:

- **Uniform sampling**: Every Nth frame. Simple, works for most tasks. `vid_stride=2` halves compute.
- **Keyframe extraction**: Detect scene changes or significant motion. Use for video summarization.
- **Adaptive sampling**: Sample more frames during activity, fewer during static periods.

```python
import cv2

def detect_scene_changes(video_path, threshold=30.0):
    cap = cv2.VideoCapture(video_path)
    prev_gray = None
    keyframes = []

    while True:
        ret, frame = cap.read()
        if not ret:
            break
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        if prev_gray is not None:
            diff = cv2.absdiff(prev_gray, gray).mean()
            if diff > threshold:
                keyframes.append(int(cap.get(cv2.CAP_PROP_POS_FRAMES)))
        prev_gray = gray

    cap.release()
    return keyframes
```

## Video Processing at Scale

### FFmpeg Pipeline

FFmpeg is the backbone of video preprocessing:

```bash
# Extract frames at 5 FPS
ffmpeg -i input.mp4 -vf "fps=5" frames/frame_%06d.jpg

# Resize and extract
ffmpeg -i input.mp4 -vf "scale=640:480,fps=10" -q:v 2 frames/%06d.jpg

# GPU-accelerated decoding
ffmpeg -hwaccel cuda -i input.mp4 -vf "fps=5" frames/%06d.jpg

# Extract specific time range
ffmpeg -ss 00:01:00 -to 00:02:00 -i input.mp4 -c copy clip.mp4
```

### GPU Video Decoding

```python
# OpenCV with CUDA backend
cap = cv2.VideoCapture('video.mp4', cv2.CAP_FFMPEG)
cap.set(cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY)

# PyAV for efficient frame access
import av
container = av.open('video.mp4')
for frame in container.decode(video=0):
    img = frame.to_ndarray(format='bgr24')
```

### Producer-Consumer Architecture for Real-Time

```python
import threading
import queue
import cv2

class VideoProcessor:
    def __init__(self, source, model, buffer_size=128):
        self.cap = cv2.VideoCapture(source)
        self.model = model
        self.frame_queue = queue.Queue(maxsize=buffer_size)
        self.result_queue = queue.Queue(maxsize=buffer_size)

    def producer(self):
        while True:
            ret, frame = self.cap.read()
            if not ret:
                self.frame_queue.put(None)
                break
            if self.frame_queue.full():
                self.frame_queue.get()  # drop oldest frame
            self.frame_queue.put(frame)

    def consumer(self):
        while True:
            frame = self.frame_queue.get()
            if frame is None:
                break
            results = self.model.predict(frame, verbose=False)
            self.result_queue.put((frame, results))

    def run(self):
        t1 = threading.Thread(target=self.producer)
        t2 = threading.Thread(target=self.consumer)
        t1.start()
        t2.start()
        t1.join()
        t2.join()
```

## Application Patterns

### Surveillance and Anomaly Detection
- Track all objects, build trajectory database
- Anomalies: unusual paths, loitering (stationary track > N seconds), wrong-way movement
- Use zone-based counting: define polygons, count track crossings

### Sports Analytics
- Player tracking with ByteTrack/BoT-SORT
- Team classification by jersey color (k-means on player crop)
- Ball tracking requires specialized detector (small, fast-moving object)
- Formation analysis from bird's-eye view (homography transformation)

### Video Summarization
- Extract keyframes via scene change detection
- Cluster similar frames, select representatives
- Combine with action recognition to select "interesting" segments

## What NOT To Do

- Do not process every frame if you do not need to. Temporal redundancy means most frames are nearly identical. Use `vid_stride` or keyframe extraction.
- Do not run detection + tracking independently without shared state. The tracker needs detection confidence scores.
- Do not use SORT in production. It loses tracks constantly during occlusion. Use ByteTrack minimum.
- Do not buffer unlimited frames in memory. Set queue size limits and drop frames if the consumer cannot keep up.
- Do not ignore camera motion. If the camera moves (pan, tilt, zoom), use BoT-SORT or add motion compensation.
- Do not assume constant frame rate from RTSP streams. Network jitter causes frame drops. Handle missing frames gracefully.
- Do not decode video at full resolution if your model input is 640x640. Decode at target resolution to save memory and bandwidth.
- Do not use `cv2.VideoCapture` in a single thread for real-time applications. Use producer-consumer pattern.
- Do not train action recognition on individual frames. Temporal context (8-32 frames minimum) is essential.
- Do not store raw video for analytics — store tracks and events. Raw video is for forensic review only.