Skip to content

01 · Getting Started

Five minutes from git clone to first result.

Prerequisites

ToolVersionWhy
Python≥ 3.10MediaPipe's prebuilt wheels cap out at 3.12; 3.13 works fine
Node.js≥ 18Electron 31 + electron-vite
Gitanyclone the repo
git-lfsrequiredbackend/models/{rtmdet,rtmpose}-m-*.onnx (104 MB + 52 MB) are Git LFS-tracked. A plain git clone gives you 134-byte pointer text files, not the real binaries — the ONNX loaders will fail. Either install git-lfs and run git lfs pull, or use scripts/fetch-model.sh (handles both MediaPipe + LFS for you).
ffmpegoptional (auto-bundled)Needed only for clip in-GUI playback. The imageio-ffmpeg pip wheel ships a static ffmpeg binary — no system install required. Without ffmpeg, the Electron GUI falls back to "seek the original video to the segment start_timecode" for unplayable clips; clips remain downloadable. See 04 · REST API.
mediapipe== 0.10.35 (pinned in backend/requirements.txt)MediaPipe 1.0 wheel has a regression on Apple Silicon that aborts in TensorsToDetectionsCalculator::Open(). See 07 · Troubleshooting. Don't pip install --upgrade mediapipe without re-reading that section.

1. Clone

bash
git clone https://github.com/leochan007/swing-analysis.git
cd swing-analysis

2. Python deps

You can either use a venv or install into your system Python — the scripts default to python3.

bash
# Option A — virtualenv (recommended)
python3 -m venv backend/.venv
backend/.venv/bin/pip install -r backend/requirements.txt

# Option B — system-wide
pip3 install -r backend/requirements.txt

3. Models

Three model files live under backend/models/, all three are Git LFS-tracked:

FileSizeSourceAfter git clone you have
pose_landmarker_lite.task5.5 MBGit LFS132-byte pointer text
rtmdet-m-487628.onnx104 MBGit LFS134-byte pointer text
rtmpose-m-27c0e6.onnx52 MBGit LFS133-byte pointer text

scripts/fetch-model.sh is the all-in-one — it (a) re-downloads the MediaPipe lite from its CDN if the real binary is missing AND LFS can't be reached, (b) detects LFS pointer files (~130 bytes, start with version https://git-lfs.github.com/spec/v1) and runs git lfs pull to materialise them:

bash
bash scripts/fetch-model.sh
# 期望输出:
#   [fetch-model] MediaPipe lite 已就位 ... (5.5M)
#   [fetch-model] 模型状态:
#     ✓ pose_landmarker_lite.task   (5.5M)
#     ✓ rtmdet-m-487628.onnx        (112M)
#     ✓ rtmpose-m-27c0e6.onnx       ( 52M)

If you'd rather do it manually:

bash
brew install git-lfs        # macOS — or apt-get install git-lfs on Linux
git lfs install
git lfs pull                # materialises the *.onnx files in backend/models/

Symptom of a missing LFS pull: the script prints 模型文件不存在: backend/models/rtmdet-m-487628.onnx — but ls -la shows the file IS there at 134 bytes. That's the LFS pointer text. Re-run bash scripts/fetch-model.sh.

4. Smoke test (no service)

bash
# Use any short video you have
python3 -m backend.cli segment \
    --video /abs/path/to/your/video.mp4 \
    --max-frames 1500 \
    --out-dir /tmp/swing_out

# expect: ✓ 完成: 检测到 N 个完整挥拍周期 + JSON: /tmp/swing_out/segments.json

4b. Run a single vendored algorithm directly

The three scripts under backend/core/ are independently runnable — no service, no pipeline shell, no Electron. Useful when you want one stage without the full orchestration:

bash
# MediaPipe 33-point once → segments + skel clips + full-video viz
backend/.venv/bin/python3 backend/core/analyze_swing.py \
    --file ../../demo.mp4 \
    --save-clips --skel-clips --viz-full

# RTMDet bbox + RTMPose 13-point skeleton, four-quadrant compositor
backend/.venv/bin/python3 backend/core/gen_skeleton_anim.py \
    --file ../../demo.mp4 \
    --det-model ../models/rtmdet-m-487628.onnx \
    --pose-model ../models/rtmpose-m-27c0e6.onnx

# Just the wrist-signal cut pipeline (same as `backend.cli segment`)
backend/.venv/bin/python3 backend/core/segment_swing.py --file ../../demo.mp4 --max-frames 1500

See 02 · Architecture for what each script is responsible for.

5. Run as a service

bash
python3 -m backend.service --port 8321
# last stdout line: SWING_SERVICE_URL=http://127.0.0.1:8321

From another terminal:

bash
curl http://127.0.0.1:8321/api/health
# {"status":"ok","version":"0.1.0","model_ready":true,...}

6. (Optional) Run the Electron GUI

bash
npm install        # 200-400 MB, takes 1-3 min
npm run dev        # compiles main + preload + renderer, opens window

The Electron app auto-spawns the Python sidecar (it expects python3 on PATH or backend/.venv/bin/python3). If you used a venv with a different name, edit src/main/index.ts's candidates array.

The window is titled Swing-Analysis and lives under the AceCrush brand (parent). The macOS top-bar app-menu slot is AceCrush; the Dock icon is the AceCrush tennis-ball logo. Detach the clips list () or the event log () into independent OS windows; press ? for the parameter reference; open ⚙ Settings from the header to configure the jobs output directory and the four annotation colours (RTMDet bbox / pose-left / pose-right / pose-body).

For fullscreen release builds:

bash
npm run pack:mac    # macOS .dmg + .zip (needs to run on macOS)
npm run pack:win    # Windows NSIS .exe (needs to run on Windows)
npm run pack:linux  # Linux AppImage + .deb (needs to run on Linux)

See 08 · Build & Package for the cross-platform build matrix and code-signing / notarization notes.

7. Backend modes (SWING_BACKEND)

The GUI ships two interchangeable analysis backends. Both drive the same main window — the differences are a few status hints and which parameter knobs are active.

bash
SWING_BACKEND=python npm run dev   # default — Python sidecar
SWING_BACKEND=ts     npm run dev   # in-renderer WASM pipeline
python (default)ts
EnginePython sidecar (uvicorn + ONNX / MediaPipe)In-renderer WASM (onnxruntime-web + MediaPipe Tasks)
Main windowSame unified UISame unified UI
Clips / vizWritten to the job directory on diskHeld in memory — play from the clip cards, download via the action-bar links
segments.jsonbackend/data/jobs/<id>/segments.jsonDownload link in the action bar
Export Package · Open Output Dir · Clear Output DirAvailableHidden (no job directory exists)
ParametersAll knobs activev_swing / max_bridge / max_lost_frames + the clip-annotation section are greyed out (ⓘ) — no ts equivalent yet
Status barSidecar warmup + per-model loadPer-model WASM load

Known limitations of ts mode: analysis runs at ~1× playback speed (a 60 s video takes about a minute), clip cutting refuses sources above 1.5 GB (in-memory encoder guard), and there is no job persistence — results live until you reset or close the window.

Where do things end up?

PathWhat's in it
backend/data/service.jsonService bind info (host/port)
backend/data/jobs/<id>/segments.jsonFinal result + full parameter echo
backend/data/jobs/<id>/clips/clip_NNN.mp4Per-cycle clips (only if save_clips=true)
backend/data/jobs/<id>/viz.mp4Color-coded phase video (only if viz_video=true)
/tmp/swing_out/CLI --out-dir (whatever you set)

All of these are gitignored. They are runtime cache; rerun rebuilds.

Next steps