Skip to content

menkelabs/camera_recorder

Repository files navigation

Dual USB Camera Recorder with Golf Swing Analysis

A Python application for capturing synchronized video from two USB cameras with integrated biomechanical analysis for golf swing evaluation. Features a Flask web-based GUI for camera configuration, recording control, analysis, and recording management.

Features

  • Dual Camera Capture: Simultaneously capture from 2 USB cameras with synchronized recording
  • Web-Based GUI: Flask-powered browser interface with tabbed layout — no desktop GUI dependencies needed
    • Live MJPEG camera preview streams
    • Camera property sliders (brightness, contrast, exposure, etc.)
    • One-click recording with real-time duration display
    • Auto-detection and re-initialization of cameras from the browser
  • Golf Swing Analysis: Integrated MediaPipe pose detection with 11 biomechanical metrics
    • Rotation: Shoulder turn, hip turn, X-factor, tempo ratio
    • Position: Lateral sway, head sway, spine angle, spine tilt
    • Body: Lead arm angle, knee flex, weight shift
    • Automatic swing phase detection (Address, Backswing, Top, Downswing, Impact, Follow-through)
    • Color-coded metric cards with good/ok/needs-work ratings
    • Interactive time-series chart with metric toggles and phase overlay
    • Frame-by-frame navigation with phase badge
    • Error details displayed in the GUI when analysis fails
  • Video Playback in Analysis: Side-by-side annotated video panels (face-on + DTL) synced to the frame slider
    • Pose skeleton overlaid on each frame
    • Play/pause with configurable speed (0.25x, 0.5x, 1x, 2x, 4x)
    • Frames stored as compressed JPEG bytes (~15 MB vs ~500 MB raw) for low memory usage
  • Auto Swing Detection: Hands-free recording via real-time shoulder-turn monitoring
    • Lightweight MediaPipe model (complexity=0) processes every 4th frame (~15 fps)
    • State machine: Idle → Motion Detected → Recording → Cooldown → Idle
    • Configurable thresholds (motion degree, confirmation frames, cooldown timer)
    • Toggle on/off from the Recording tab; hides manual start/stop when active
    • Real-time shoulder-turn gauge and state badge in the UI
  • Swing Comparison: Side-by-side comparison of any two recorded swings
    • Delta cards showing value changes between swings with color-coded indicators
    • Overlay chart with normalised timeline (swings of different lengths align on 0-100%)
    • Dashed vs solid lines for easy visual distinction
  • Recording Management: Browse, inspect, and delete recordings from the GUI
    • View all recordings with date, duration, and file sizes
    • Delete individual recordings or bulk-select and delete
    • Age-based cleanup (delete recordings older than N days)
  • Archive to External Disk: One-click archiving of recordings to a configured USB drive
    • Configure the archive path once in the Settings tab (e.g. /media/user/Seagate8TB/golf)
    • Archives video files, analysis JSON, and camera settings
    • Tracks which recordings have been archived to avoid duplicates
    • Shows external disk space (total/used/free) with a usage bar
    • Connected/disconnected status badge for the archive drive
  • Multiple Recording Sessions: Configure, record, analyze, and record again without restarting
  • Platform Support: Windows and Linux with platform-appropriate camera backends and defaults
    • Auto-detection of camera indices with fallback search
    • Separate capture threads per camera for reliable dual-cam streaming on Linux
  • 120 FPS Recording Target: Optimized for high frame-rate capture
    • Threaded per-camera capture
    • Efficient video codecs (H.264, XVID)
    • Minimal buffering to reduce memory overhead

Installation

  1. Install Python 3.7 or higher

  2. Install dependencies:

pip install -r requirements.txt

Dependencies include: opencv-python, numpy, mediapipe, Pillow, flask

Quick Start

Run the Flask GUI:

python scripts/flask_gui.py

Then open http://localhost:5000 in your browser.

With explicit camera IDs:

# Linux (find your camera indices with the Detect button in the GUI)
python scripts/flask_gui.py --camera1 0 --camera2 2

# Windows (auto-detected from config_windows.json, or specify manually)
python scripts/flask_gui.py --camera1 0 --camera2 2

Command Line Options

python scripts/flask_gui.py [OPTIONS]

Options:
  --camera1 ID            Camera 1 index (default: 0 on Linux, from config on Windows)
  --camera2 ID            Camera 2 index (default: 1 on Linux, from config on Windows)
  --width WIDTH           Resolution width (default: 1280)
  --height HEIGHT         Resolution height (default: 720)
  --fps FPS               Recording FPS target (default: 120)
  --model-complexity {0,1,2}  MediaPipe model for analysis: 0=lite (fast), 1=full, 2=heavy (default: 2)
  --host HOST             Host to bind (default: 0.0.0.0)
  --port PORT             Port (default: 5000)

For lower-end hardware (e.g. HP EliteBook 840 G5), use --model-complexity 0 for faster analysis at the cost of slightly lower accuracy.

Usage

GUI Overview

The application provides a tabbed web interface with 7 tabs:

  1. Camera 1 Setup [1]: Live preview + property sliders (brightness, contrast, exposure, etc.)
  2. Camera 2 Setup [2]: Same controls for the second camera
  3. Recording [3]: Dual camera live preview, start/stop recording with Space bar
  4. Recordings [4]: Browse, manage, and delete saved recordings
  5. Analysis [5]: View analysis results with frame-by-frame navigation
  6. Compare [6]: Side-by-side comparison of any two analyzed swings
  7. Settings [7]: Archive configuration and external disk management

Workflow

  1. Configure Cameras (Tabs 1 & 2):

    • Adjust camera properties with sliders in the browser
    • Save settings or reset to defaults with buttons
    • Use Detect in the header to find available camera indices
    • Use Reinit to re-open cameras after plugging in or changing indices
  2. Record (Tab 3):

    • Click Start Recording or press Space to start/stop
    • Auto Detect toggle: enable to let the system watch for swing motion and auto-start/stop recording
    • When Auto Detect is on, a shoulder-turn gauge and state badge show real-time detection status
    • Recordings save to the recordings/ directory automatically
  3. Manage Recordings (Tab 4):

    • View all recordings with date, duration, and file sizes
    • Delete individual recordings or select multiple for bulk delete
    • Clean up old recordings by age (e.g., delete everything older than 30 days)
  4. Analyze (Tab 5 - Automatic):

    • Analysis starts automatically after recording completes
    • Results are auto-saved as JSON alongside the video files for later comparison
    • Video playback panels show annotated frames (pose skeleton) for both cameras, synced to the slider
    • Play/pause button with speed control (0.25x – 4x); press Space on the Analysis tab
    • Navigate frames with A/Left Arrow (previous) and D/Right Arrow (next)
    • If analysis fails, the error reason is displayed in the tab
  5. Compare (Tab 6):

    • Select any two previously analyzed swings from the dropdowns
    • See delta cards showing which metrics improved or regressed
    • View overlaid time-series with normalised x-axis (swing progress 0-100%)
  6. Archive (Tab 7 — Settings):

    • Set the archive path to your external USB drive (e.g. /media/username/Seagate8TB/golf)
    • Click Archive All New Recordings to copy videos, analysis JSON, and settings to the drive
    • The disk usage bar shows total/used/free space; a badge shows connected/disconnected status

Keyboard Shortcuts

Key Action
1-7 Switch between tabs
Space Start/stop recording (Recording tab) or play/pause video (Analysis tab)
A / Left Arrow Previous analysis frame (on Analysis tab)
D / Right Arrow Next analysis frame (on Analysis tab)

Camera Header Controls

The header bar shows per-camera status and provides:

  • Cam 1 / Cam 2 index inputs — set which indices to open
  • Reinit — Re-open cameras with the specified indices (no restart needed)
  • Detect — Scan indices 0-7 to find available cameras, auto-fills the inputs
  • Re-initialize — Re-open cameras with the current indices

Output

Recorded videos are saved in the recordings/ directory:

  • recording_YYYYMMDD_HHMMSS_camera1.mp4
  • recording_YYYYMMDD_HHMMSS_camera2.mp4

Analysis results include:

  • Camera 1 (face-on): Lateral sway, head sway, spine tilt, knee flex, weight shift
  • Camera 2 (down-the-line): Shoulder turn, hip turn, X-factor, spine angle, lead arm angle
  • Swing phase detection (Address, Backswing, Top, Downswing, Impact, Follow-through)
  • Tempo ratio (backswing / downswing frames, pros average ~3:1)
  • Detection rates for each camera
  • Per-frame values for navigation + time-series chart
  • JSON results auto-saved for later comparison

Analysis Dashboard

The Analysis tab displays a rich dashboard with 5 sections:

+----------------------------+  +----------------------------+
| Camera 1 — Face-On         |  | Camera 2 — Down-the-Line   |
|                            |  |                            |
|   [annotated frame with    |  |   [annotated frame with    |
|    pose skeleton overlay]  |  |    pose skeleton overlay]  |
|                            |  |                            |
+----------------------------+  +----------------------------+

+----------------------------------------------------------+
| ▶ 1x  < Prev  Frame: 42/180  [======|====]  Next >  TOP |  <- play/pause + speed
+----------------------------------------------------------+

+---------------+  +---------------+  +---------------+  +---------------+
| SHOULDER TURN |  | HIP TURN      |  | X-FACTOR      |  | TEMPO         |
|    +45.2°     |  |    +22.1°     |  |    23.1°      |  |    3.1:1      |
| [=======  ]   |  | [=====    ]   |  | [======   ]   |  | [======   ]   |
| Max: +92.1°   |  | Max: +38.5°   |  | Max: 53.6°    |  | Ratio: 3.1:1  |
+---------------+  +---------------+  +---------------+  +---------------+
+---------------+  +---------------+  +---------------+  +---------------+
| LATERAL SWAY  |  | HEAD SWAY     |  | SPINE ANGLE   |  | SPINE TILT    |
|    +12px      |  |    -3px       |  |    32.1°      |  |    +5.2°      |
| [=======  ]   |  | [========]    |  | [======   ]   |  | [======   ]   |
| Max R: 34px   |  | Max R: 8px    |  | Addr: 30.5°   |  | Max: +12.3°   |
+---------------+  +---------------+  +---------------+  +---------------+
+---------------+  +---------------+  +---------------+
| LEAD ARM      |  | KNEE FLEX     |  | WEIGHT SHIFT  |
|    172.3°     |  |    165.1°     |  |    62%        |
| [=========]   |  | [========]    |  | [======   ]   |
| Min: 168.5°   |  | Addr: 168.0°  |  | Max Fwd: 72%  |
+---------------+  +---------------+  +---------------+

+----------------------------------------------------------+
| Time-Series Chart                    [Shoulder] [Sway] ..|
|                                                          |
|   ~~~/\~~~~                                              |
|  /        \___           |  <- current frame indicator   |
| /             \~~~~~/    |                                |
+----------------------------------------------------------+

Camera 1 (Face-On)              Camera 2 (Down-the-Line)
  Detection: 98.5%                Detection: 97.2%
  Max Sway Left: 28px            Max Shoulder: +92.1°
  Max Head Sway R: 8px           Max Hip: +38.5°
  ...                             ...

Each metric card is color-coded:

  • Green bar: Good range (e.g., X-factor 30-55°)
  • Yellow bar: Acceptable range
  • Red bar: Needs improvement

Swing Comparison Display

The Compare tab lets you pick any two analyzed swings and see the differences:

  Swing A: 2026-02-15 14:30:22        vs        Swing B: 2026-02-15 14:35:10

+------------------+  +------------------+  +------------------+  +------------------+
| SHOULDER TURN    |  | HIP TURN         |  | X-FACTOR         |  | TEMPO            |
| +85.2°  → +92.1° |  | +35.0° → +38.5° |  | 50.2°  → 53.6°  |  | 2.8:1  → 3.1:1  |
|          +6.9    |  |          +3.5    |  |          +3.4    |  |          +0.3    |
+------------------+  +------------------+  +------------------+  +------------------+
  ...

+----------------------------------------------------------+
| Overlay Chart               [Shoulder] [Sway] [X-Factor] |
|                                                          |
|  --- Swing A (dashed)                                    |
|  ___ Swing B (solid)                                     |
|                                                          |
|  ~~~/\~~~~                                               |
| /        \___                                            |
+----------------------------------------------------------+

Delta indicators:

  • Green (+): Metric improved from A to B
  • Red (-): Metric regressed
  • Gray: No significant change

API Endpoints

The Flask GUI exposes a REST API (used by the browser UI):

Method Endpoint Description
GET / Serve the web UI
GET /video_feed/<cam> MJPEG live stream
GET /api/status System status (cameras, recording, analysis)
GET /api/camera/<cam>/properties Camera property values and ranges
POST /api/camera/<cam>/property Set a camera property
POST /api/camera/<cam>/reset Reset camera properties to defaults
POST /api/settings/save Save camera settings to JSON
POST /api/cameras/reinit Re-initialize cameras (optional new IDs)
POST /api/cameras/detect Detect available camera indices
POST /api/recording/start Start recording
POST /api/recording/stop Stop recording + trigger analysis
GET /api/recordings List all recordings with metadata
GET /api/recordings/stats Recording count, disk usage, oldest/newest
DELETE /api/recordings/<timestamp> Delete a recording pair
DELETE /api/recordings Bulk delete recordings
POST /api/recordings/cleanup Delete recordings older than N days
GET /api/analysis/results Get analysis results and frame data
POST /api/analysis/frame Set the current analysis frame index
GET /api/analysis/frame/<cam>?index=N Get annotated JPEG frame for camera at index
POST /api/auto-detect/toggle Enable/disable auto swing detection
GET /api/auto-detect/status Get auto-detect state and shoulder-turn values
GET /api/analyses List all saved analysis results
GET /api/compare?a=TS&b=TS Compare two swings by timestamp
GET /api/archive/config Get archive path and disk status
POST /api/archive/config Set the archive path
GET /api/archive/status Archived recordings count and disk info
POST /api/archive/run Archive all new (or specified) recordings

Performance Tips

  1. Resolution: 720p (1280x720) provides a good balance between quality and performance
  2. Frame Rate: 120 FPS is the default target; reduce if CPU usage is high
  3. USB Bandwidth: Two identical USB cameras must be on different USB buses (different physical controllers/hubs) to stream simultaneously. If camera 2 opens but shows no frames, move it to a different USB port.
  4. Hardware Acceleration: The app automatically tries hardware-accelerated codecs when available
  5. Close Other Applications: Free up CPU and USB bandwidth for camera capture
  6. Model Complexity: Use --model-complexity 0 (lite) on laptops like the HP EliteBook 840 G5 for ~3x faster analysis. Use 2 (heavy) on workstations for best accuracy.
  7. Memory: Annotated frames are stored as compressed JPEG (~50 KB each) instead of raw BGR (~2.7 MB each), keeping analysis memory under 20 MB for a typical 300-frame recording.
  8. Auto-Detect: The swing detector uses model complexity 0 and only processes every 4th capture frame, adding minimal CPU overhead during preview.

Platform Differences

Windows

  • Uses DirectShow backend (cv2.CAP_DSHOW) for better camera compatibility
  • Camera configuration stored in config_windows.json (auto-detected)
  • To regenerate camera config: python scripts/detect_windows_cameras.py
  • Default cameras: From config_windows.json if available, otherwise 0 and 2

Linux

  • Uses default OpenCV backend (V4L2)
  • Separate capture thread per camera for reliable dual-cam streaming
  • 1.5s delay between opening cameras + warmup reads (required for V4L2 with identical USB cams)
  • Auto-fallback: if the requested camera 2 index fails, tries other indices automatically
  • Default cameras: 0 and 1

See docs/PLATFORM_CONFIG.md for detailed platform configuration information.

Troubleshooting

Camera Not Found

  • Use the Detect button in the GUI header to find available indices
  • Windows: Run python scripts/detect_windows_cameras.py to detect cameras
  • Ensure cameras are not being used by other applications
  • Run python tests/test_cameras.py to find available cameras

Camera 2 Opens But No Video

  • Most likely cause: Both cameras are on the same USB bus (bandwidth conflict)
  • Fix: Move one camera to a different USB hub/controller (check with lsusb -t)
  • The GUI will show "Camera 2 not available" in the video feed even if isOpened() returns true

High CPU Usage

  • Reduce resolution (try 640x480)
  • Reduce FPS target (try 30 or 60)
  • Close other applications

Recording Not Working

  • Check if recordings/ directory exists and is writable
  • Verify cameras are providing frames (check console output)

Analysis Errors

  • Errors now display directly in the Analysis tab with the specific error message
  • Ensure MediaPipe is installed: pip install mediapipe
  • Analysis runs even if no poses are detected (detection rate will be 0%)

Testing

# Flask GUI tests (routes, template, recording, analysis, video playback, auto-detect)
python -m pytest tests/test_flask_gui.py -v

# Swing detector state machine (idle, motion, recording, cooldown, full cycle)
python -m pytest tests/test_swing_detector.py -v

# Swing metrics (all 11 metrics, phases, tempo, analyze_sequence)
python -m pytest tests/test_sway_calculator.py -v

# Swing comparison (save/load JSON, compare API)
python -m pytest tests/test_swing_comparison.py -v

# Recording management (list, delete, bulk delete, cleanup)
python -m pytest tests/test_recording_management.py -v

# Archive to external disk (config, copy, manifest, API)
python -m pytest tests/test_archive.py -v

# Analysis navigation and workflow
python -m pytest tests/test_analysis_navigation.py -v
python -m pytest tests/test_analysis_workflow.py -v

# GUI unit tests (OpenCV-based, legacy)
python -m pytest tests/test_gui.py -v

# Workflow tests
python -m pytest tests/test_config_to_record_workflow.py -v

# Camera detection
python tests/test_cameras.py

# Run all tests
python run_all_tests.py

Technical Details

  • Architecture: Flask web server with MJPEG streaming, REST API, and single-page HTML/JS frontend
  • Threading: Separate capture thread per camera to avoid V4L2 contention
  • Buffering: Minimal buffering (buffer size 1) to reduce latency
  • Codecs: Automatically selects best available codec (H.264 > XVID > mp4v)
  • Analysis: MediaPipe Pose Detection (configurable model complexity 0/1/2) with 15 landmark extraction and 11 biomechanical metrics
  • Video Playback: Annotated frames stored as compressed JPEG bytes for low-memory playback (~15 MB for 300 frames)
  • Auto-Detect: SwingDetector state machine using lightweight MediaPipe model (complexity=0) with frame subsampling
  • Swing Comparison: JSON-serialized analysis results with normalised overlay charting
  • Resource Management: Automatic camera release/reacquisition between recording sessions
  • Recording Management: File-based storage with timestamp grouping and safe deletion
  • Archive: Configurable external disk path with manifest-based deduplication and disk usage reporting

Project Structure

See PROJECT_STRUCTURE.md for detailed project organization.

License

Free to use and modify for your projects.

About

Dual camera position analysis via Mediapipe

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •