# table_tennis_models **Repository Path**: oftenlin/table_tennis_models ## Basic Information - **Project Name**: table_tennis_models - **Description**: table_tennis_models - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-20 - **Last Updated**: 2026-06-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AI Table Tennis - Ball Tracking & Rally Detection An end-to-end pipeline for table tennis video analysis: detect the ball in every frame with TrackNet V2, identify rally segments with a TCN classifier, and export highlight clips with ball trajectory overlay. ## Pipeline Stages ``` Stage 1 Stage 2 Stage 3 Stage 4 Stage 5 ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Ubuntu 训练 │ ──► │ PT 业务评估 │ ──► │ CoreML 转换 │ ──► │ MacCLI 评估 │ ──► │ QTracker 集成 │ │ │ │ │ │ │ │ (性能/精度) │ │ │ │ TrackNet V2 │ │ run_pt_ │ │ tracknet_ │ │ │ │ iOS App │ │ Rally TCN │ │ pipeline.py │ │ coreml_ │ │ MacCLI + │ │ 依赖 Core │ │ │ │ │ │ convert.py │ │ diff_3way.py │ │ 库 │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ TrackNetV2PyTorch/ pipeline/ ModelConvert/ iOSApps/MacCLI/ iOSApps/QTracker/ RallyDetectModel/ ``` ## Project Structure ``` ai_table_tennis/ │ ├── iOSApps/ # 所有 Swift/iOS 项目 │ ├── Core/ # 共享 SPM 库 (TennisTracker) │ │ ├── Package.swift │ │ ├── Models/ # TrackNetV2.mlpackage, rally_v9.mlpackage │ │ ├── Sources/TennisTracker/ │ │ │ ├── TrackNet/ # 球轨迹检测推理 │ │ │ ├── Rally/ # 回合分割推理 │ │ │ ├── Video/ # 视频 I/O 工具 │ │ │ └── Pipeline/ # 高层编排 (PipelineRunner) │ │ └── Tests/TennisTrackerTests/ # 单元测试 │ │ │ ├── BallTrackerDemo/ # POC 验证 App(原 iOSApp) │ │ ├── iOSApp.xcodeproj │ │ └── Sources/ # ContentView, Highlights/ │ │ │ ├── QTracker/ # 正式版 iOS App(「球迹」) │ │ ├── QTracker.xcodeproj # 依赖 Core (TennisTracker) │ │ └── Sources/ # AIClipFlowView, LocalRecordingStore, ... │ │ │ └── MacCLI/ # macOS CLI 批量处理工具 │ ├── Package.swift # 依赖 TennisTracker │ ├── Sources/main.swift │ └── build.sh │ ├── pipeline/ # 业务评估管线 │ ├── run_pt_pipeline.py # Stage 2: Ubuntu PT 全流程推理 │ ├── diff_3way.py # Stage 4: 多路结果数值对比 │ └── run_3way_validation.sh # Stage 2-4: 一键编排(同步/转换/推理/对比) │ ├── ModelConvert/ # PyTorch → CoreML 转换 │ ├── tracknet_coreml_convert.py │ ├── rallytcn_coreml_convert.py │ └── outputs/ # 转换产物 (.mlpackage) │ ├── model-registry/ # 模型版本注册中心 │ ├── registry.json # 版本索引(App 通过 Core bundle 读取) │ ├── versions/ # 每版本一个目录(manifest + metrics + notes) │ └── ground-truth/ # 真值数据(rally/ + tracker/) │ ├── TrackNetV2PyTorch/ # Stage 1: TrackNet 训练 (PyTorch) ├── RallyDetectModel/ # Stage 1: Rally TCN 训练 (PyTorch) ├── data/ # 测试视频 + 标注 ├── weights/ # 模型权重 (.pt) └── docs/ ``` ## Core SPM Package 位于 `iOSApps/Core/`,编译为 `TennisTracker` 库,被 MacCLI、BallTrackerDemo、QTracker 共同依赖。所有模型文件 (`.mlpackage`) 和 Metal shader 作为 SPM resources 打包。 ### Tests ```bash cd iOSApps/Core swift test # 6 个测试:模块加载、PipelineConfig 默认值、RallySegment、PerFrameTrackNetResult、resourceBundle、ModelRegistry ``` ### TrackNet Module (`Core/Sources/TennisTracker/TrackNet/`) Ball detection using TrackNet V2 (VGG16 + UNet architecture). Processes video frame-by-frame, outputting ball coordinates per frame. | File | Purpose | |------|---------| | `TrackNetCoreML.swift` | Wraps CoreML model loading and inference | | `TrackNetCollector.swift` | Public API. Reads video via AVAssetReader, batches 3 frames for 9-channel input, runs inference, decodes heatmaps. Supports `maxFrames` limit and progress callbacks. | | `MetalPreprocessor.swift` | GPU preprocessing: BGRA pixel buffer → NCHW fp16 MLMultiArray | | `HeatmapDecoder.swift` | vDSP-based argmax on 3-channel heatmap output | | `TrackNetPreprocess.metal` | Metal compute kernel: swizzle BGRA→RGB, normalize, pack as fp16 | | `DiagnosticsLogger.swift` | Async file-based diagnostic logging | ```swift public struct PerFrameTrackNetResult { public let frameIndex: Int public let x: Float // Ball x in original video coords public let y: Float // Ball y in original video coords public let visibility: Float // 1.0 = ball detected, 0.0 = missing public let heatmapMax: Float // Confidence score (0-1) } ``` ### Rally Module (`Core/Sources/TennisTracker/Rally/`) Rally segmentation using a TCN (Temporal Convolutional Network). | File | Purpose | |------|---------| | `RallyPipeline.swift` | Public API. Orchestrates feature building → inference → postprocessing | | `RallyFeatureBuilder.swift` | Builds 24-dim feature vectors per frame, mirroring Python io_utils.py | | `RallyInferenceController.swift` | Sliding-window TCN inference via CoreML | | `RallyPostprocessor.swift` | Median filter → threshold → merge gaps → filter by min length | | `RallySegment.swift` | Simple struct: `startFrame`, `endFrame`, JSON-serializable | ```swift public struct RallySegment { public let startFrame: Int public let endFrame: Int public var dict: [String: Int] // JSON-ready } ``` ### Video Module (`Core/Sources/TennisTracker/Video/`) | File | Purpose | |------|---------| | `VideoOverlayWriter.swift` | Renders ball position dots and trail lines onto video frames via AVAssetWriter | | `VideoTrimmer.swift` | Frame-accurate video clipping using AVAssetExportSession | | `PlatformVideoPlayer.swift` | Cross-platform AVPlayer wrapper (macOS/iOS) | ### Pipeline Module (`Core/Sources/TennisTracker/Pipeline/`) | File | Purpose | |------|---------| | `PipelineRunner.swift` | Single-video end-to-end pipeline: TrackNet → Rally → Export. Returns `VideoOutput` | | `PipelineConfig.swift` | All tunable parameters aligned with run_pt_pipeline.py | **PipelineConfig parameters:** | Parameter | Default | Description | |-----------|---------|-------------| | `maxFrames` | 0 (all) | Max frames to process | | `rallyWindow` | 1024 | TCN sliding window size | | `rallyStride` | 256 | Sliding window stride | | `threshold` | 0.5 | Rally probability threshold | | `minLen` | 35 | Min rally segment (frames) | | `mergeGap` | 25 | Max gap to merge adjacent segments | | `smoothWidth` | 9 | Median filter kernel width | | `exportClips` | false | Export rally clip .mp4 files | | `bufferSec` | 1.0 | Seconds before/after each rally clip | | `overlayVideo` | false | Generate ball-tracked overlay video | | `saveCSV` | false | Write per-frame TrackNet CSV | | `saveJSON` | false | Write rally segments JSON | ## MacCLI macOS 命令行批量处理工具,替代了原来的 Python CoreML 推理 (`run_coreml_pipeline.py`)。参数命名对齐 `run_pt_pipeline.py`。 位于 `iOSApps/MacCLI/`。 ### Build & Run ```bash cd iOSApps/MacCLI bash build.sh # → .build/arm64-apple-macosx/release/MacCLI # 单视频推理 .build/arm64-apple-macosx/release/MacCLI \ --video-dir ../../data/video \ --out-dir ./output \ --tracknet-coreml ../Core/Models/TrackNetV2.mlpackage \ --rally-coreml ../Core/Models/rally_v9.mlpackage \ --videos IMG_3466 \ --save-csv --save-json # 完整流水线:覆盖视频 + 导出回合片段 .build/arm64-apple-macosx/release/MacCLI \ --video-dir ../../data/video \ --out-dir ./output \ --tracknet-coreml ../Core/Models/TrackNetV2.mlpackage \ --rally-coreml ../Core/Models/rally_v9.mlpackage \ --overlay --export-clips \ --buffer-sec 1.0 \ --save-csv --save-json ``` ### All CLI Options | Flag | Description | |------|-------------| | `--video-dir ` | Directory with input videos **(required)** | | `--out-dir ` | Output directory **(required)** | | `--tracknet-coreml ` | Path to TrackNetV2.mlpackage | | `--rally-coreml ` | Path to rally_v9.mlpackage | | `--videos ` | Comma-separated video names (default: all) | | `--max-frames ` | Max frames to process (0 = all) | | `--threshold ` | Rally probability threshold | | `--min-len ` | Minimum rally segment in frames | | `--merge-gap ` | Max gap to merge adjacent rallies | | `--smooth-width ` | Median filter window width | | `--overlay` | Generate ball-trajectory overlay .mp4 | | `--export-clips` | Export rally highlight clips | | `--buffer-sec ` | Buffer seconds around clips | | `--save-csv` | Write per-frame TrackNet CSV | | `--save-json` | Write rally segments JSON | | `--metallib-path ` | Precompiled .metallib (optional) | ### Output Files ``` output/ # --out-dir ├── IMG_3466_tracks.csv # Per-frame: frame_index, visibility, x, y, heatmap_max ├── IMG_3466_segments.json # Rally segments: [{startFrame, endFrame}, ...] ├── IMG_3466_tracked.mp4 # Overlay video (--overlay) └── clips/ ├── IMG_3466_clip_000.mp4 ├── IMG_3466_clip_001.mp4 └── ... ``` ## BallTrackerDemo (POC App) 位于 `iOSApps/BallTrackerDemo/`,SwiftUI iOS App 用于快速验证 Core 库的 PipelineRunner。 - Video Selection: 从相册或文件选取视频 - Processing: 运行完整 TrackNet → Rally → Clip 管线,带进度 UI - Preview: 内嵌视频播放器预览跟踪叠加和剪辑片段 - Export: 系统分享菜单导出处理结果 ## QTracker (Production App) 位于 `iOSApps/QTracker/`,正式版 iOS App「球迹」。功能包括: - **比赛录制** / **动作录制**:相机采集视频 - **AI 精彩剪辑**:依赖 Core 库,自动检测球轨迹 → 识别回合 → 剪辑高光片段 - **本地录制管理**:持久化录像索引,支持未剪辑/已剪辑/剪辑中状态追踪 - **手动导入**:从文件或相册导入外部视频进行分析 QTracker 删除了原有的 14 个 AIHighlights 文件(约 23MB 重复代码和模型),改为通过 SPM 依赖 Core/TennisTracker 库。保留自己的参数偏好(smoothWidth=21, minLen=30, mergeGap=45)。 ## Pipeline Scripts 位于 `pipeline/`,用于跨平台业务评估和模型验证。 | 脚本 | 用途 | |------|------| | `run_pt_pipeline.py` | Ubuntu PyTorch 全流程推理(TrackNet + Rally TCN) | | `diff_3way.py` | 多路推理结果数值对比(Ubuntu PT / Mac CoreML FP16 / Mac CoreML FP32) | | `run_3way_validation.sh` | 一键编排:同步代码+权重 → Mac 上转换 CoreML → Ubuntu PT 推理 → Mac MacCLI 推理 → 拉回结果对比 | ### 3-Way Validation ```bash # 前提:Mac 已设置 SSH 免密登录 bash pipeline/run_3way_validation.sh ``` 流程:SSH 同步代码到 Mac → Mac 上 CoreML 模型转换 → 本地 Ubuntu PT 推理 → Mac 上 MacCLI 推理 (FP16 + FP32) → 拉回结果 → 三路 diff。 ## Python Modules ### TrackNetV2PyTorch/ | File | Purpose | |------|---------| | `models/tracknet.py` | VGG16 + UNet model definition | | `train.py` | Training loop with YOLO-format dataset | | `detect.py` | PyTorch inference on video files | | `val.py` | Validation metrics | | `tracknet_video_tracks_io.py` | Read/write track CSV files | | `tools/prepare_table_tennis_dataset.py` | Dataset preparation | ### RallyDetectModel/ | File | Purpose | |------|---------| | `rally_net/models.py` | TCN model architecture | | `rally_net/io_utils.py` | Feature building (24-dim) — reference for Swift RallyFeatureBuilder | | `rally_net/postprocess.py` | Median filter + binarize + merge — reference for Swift RallyPostprocessor | | `train_rally.py` | Training script | | `eval_rally.py` | Evaluation metrics | | `infer_rally.py` | Single-video inference | | `export_pred_rally_clips.py` | Full Python pipeline: infer → segment → export clips | ### ModelConvert/ | File | Purpose | |------|---------| | `tracknet_coreml_convert.py` | TrackNet V2: PyTorch → CoreML (.mlpackage) | | `rallytcn_coreml_convert.py` | Rally TCN: PyTorch → CoreML (.mlpackage) | | `outputs/` | 转换产物 (.mlpackage),供 pipeline 验证使用 | ## 命令速查 ### 环境要求 - macOS 13+ / iOS 16+ - Xcode 26+ (Swift 5.9) - Python 3.10+ (仅训练/转换需要) ### Core 库编译 & 测试 ```bash cd iOSApps/Core swift build # Debug 编译 swift build -c release # Release 编译 swift test # 运行单元测试 ``` ### MacCLI 编译与运行 ```bash cd iOSApps/MacCLI bash build.sh # 快速测试 .build/arm64-apple-macosx/release/MacCLI \ --video-dir ../../data/video \ --out-dir ./test_output \ --tracknet-coreml ../Core/Models/TrackNetV2.mlpackage \ --rally-coreml ../Core/Models/rally_v9.mlpackage \ --videos IMG_3466 \ --max-frames 2000 \ --overlay --save-csv --save-json ``` ### BallTrackerDemo 编译 ```bash cd iOSApps/BallTrackerDemo xcodebuild -project iOSApp.xcodeproj -scheme iOSApp \ -destination 'platform=iOS Simulator,name=iPhone 17' build ``` ### QTracker 编译 ```bash cd iOSApps/QTracker xcodebuild -project QTracker.xcodeproj -scheme QTracker \ -destination 'platform=iOS Simulator,name=iPhone 17' build ``` ### Python 训练 ```bash # TrackNet V2 cd TrackNetV2PyTorch pip install -r requirements.txt python train.py --data data/table_tennis.yaml --epochs 100 --batch-size 4 # Rally TCN cd ../RallyDetectModel python train_rally.py --epochs 200 --batch-size 64 ``` ### PyTorch → CoreML 转换 ```bash cd ModelConvert python tracknet_coreml_convert.py \ --weights ../weights/tracknet_v2_best.pt \ --output ../iOSApps/Core/Models/TrackNetV2.mlpackage python rallytcn_coreml_convert.py \ --weights ../weights/rally_model.pt \ --output ../iOSApps/Core/Models/rally_v9.mlpackage ``` ### PT 全流程推理 ```bash python pipeline/run_pt_pipeline.py \ --tracknet-pt weights/tracknet_v2_best.pt \ --rallytcn-pt weights/rally_model.pt \ --video-dir data/video \ --out-dir data/validate_3way/ubuntu_pt \ --device cuda ``` ## Model Version Registry 模型版本注册中心,位于 `model-registry/`。每次模型发布时创建版本目录,记录产物清单、MD5 校验值、训练/业务指标和变更说明。 ### 版本目录结构 ``` model-registry/ ├── registry.json # 版本索引(App 可读,打包进 Core SPM resource) ├── versions/ │ └── v1.0.0/ │ ├── manifest.json # 产物清单 + 所有文件 MD5(.pt / .mlpackage) │ ├── metrics.json # 训练指标 + 业务指标(以真值为准)+ 跨平台对比 │ └── notes.md # 解决的问题、思路、决策记录 └── ground-truth/ # 真值数据 ├── rally/ # {video_name}.json(每段 {startFrame, endFrame}) └── tracker/ # {video_name}.csv(每帧 {x, y, visibility}) ``` ### registry.json(App 可读) Core 库内置 `ModelRegistry` 类型,从 bundle 中的 `registry.json` 读取当前模型版本和 MD5 信息: ```swift if let registry = ModelRegistry.load() { print(registry.latest) // "1.0.0" print(registry.tracknet.md5) // "a3381bab..." print(registry.rally.md5) // "47142537..." } ``` ### 模型发布流程(详细操作) 以下为新版本 `vX.Y.Z` 的完整发布操作流程。**所有步骤均在 Ubuntu 上执行,只在 CoreML 转换和 MacCLI 评测时 SSH 到 Mac。** #### Step 1: Ubuntu — 训练 & PT 评测 ```bash # TrackNet V2 训练 cd TrackNetV2PyTorch python train.py --data data/table_tennis.yaml --epochs 100 --batch-size 4 # 产出 weights/tracknet_v2_best.pt # Rally TCN 训练 cd ../RallyDetectModel python train_rally.py --epochs 200 --batch-size 64 # 产出 weights/rally_model.pt # PT 推理 + 评测 python pipeline/run_pt_pipeline.py \ --tracknet-pt weights/tracknet_v2_best.pt \ --rallytcn-pt weights/rally_model.pt \ --video-dir data/video \ --out-dir data/validate_3way/ubuntu_pt \ --device cuda ``` #### Step 2: Ubuntu — 计算 PT 权重 MD5 ```bash md5sum weights/tracknet_v2_best.pt weights/rally_model.pt # 记录输出,稍后填入 manifest.json ``` #### Step 3: Ubuntu → Mac — SCP 权重 + CoreML 转换 ```bash # 同步代码到 Mac SSH_USER="user" MAC_IP="192.168.1.x" MAC_WORKSPACE="/path/to/mac/ai_table_tennis" rsync -avz --delete \ --exclude '.git' --exclude '*.pyc' --exclude '__pycache__' \ ./ $SSH_USER@$MAC_IP:$MAC_WORKSPACE/ # 在 Mac 上执行 CoreML 转换 ssh $SSH_USER@$MAC_IP " cd $MAC_WORKSPACE/ModelConvert python tracknet_coreml_convert.py \ --weights ../weights/tracknet_v2_best.pt \ --output outputs/TrackNetV2_fp16.mlpackage python tracknet_coreml_convert.py \ --weights ../weights/tracknet_v2_best.pt \ --output outputs/TrackNetV2_fp32.mlpackage \ --fp32 python rallytcn_coreml_convert.py \ --weights ../weights/rally_model.pt \ --output outputs/RallyTCN.mlpackage " ``` #### Step 4: Mac — 计算 CoreML MD5 + MacCLI 评测 ```bash # 计算每个 .mlpackage 的 MD5(对目录内所有文件排序后取 hash) ssh $SSH_USER@$MAC_IP " cd $MAC_WORKSPACE for dir in ModelConvert/outputs/*.mlpackage; do echo \"=== \$dir ===\" find \"\$dir\" -type f -exec md5 -r {} \\; | sort | md5 done # 编译 MacCLI cd iOSApps/MacCLI && swift build -c release # MacCLI FP16 推理 .build/arm64-apple-macosx/release/MacCLI \ --video-dir ../data/video \ --out-dir ./output_fp16 \ --tracknet-coreml ../ModelConvert/outputs/TrackNetV2_fp16.mlpackage \ --rally-coreml ../ModelConvert/outputs/RallyTCN.mlpackage \ --save-csv --save-json # MacCLI FP32 推理 .build/arm64-apple-macosx/release/MacCLI \ --video-dir ../data/video \ --out-dir ./output_fp32 \ --tracknet-coreml ../ModelConvert/outputs/TrackNetV2_fp32.mlpackage \ --rally-coreml ../ModelConvert/outputs/RallyTCN.mlpackage \ --save-csv --save-json " ``` #### Step 5: Mac → Ubuntu — 回传产物 ```bash # 拉回 mlpackage 产物 scp -r $SSH_USER@$MAC_IP:$MAC_WORKSPACE/ModelConvert/outputs/*.mlpackage ModelConvert/outputs/ # 拉回 MacCLI 推理结果 scp -r $SSH_USER@$MAC_IP:$MAC_WORKSPACE/iOSApps/MacCLI/output_fp16 data/validate_3way/mac_fp16 scp -r $SSH_USER@$MAC_IP:$MAC_WORKSPACE/iOSApps/MacCLI/output_fp32 data/validate_3way/mac_fp32 ``` #### Step 6: Ubuntu — 跨平台对比 + 业务评测 ```bash # 三路 diff(PT vs CoreML FP16 vs CoreML FP32) python pipeline/diff_3way.py \ --pt data/validate_3way/ubuntu_pt \ --fp16 data/validate_3way/mac_fp16 \ --fp32 data/validate_3way/mac_fp32 # 业务评测(如果有真值数据) # python pipeline/eval_business.py \ # --predictions data/validate_3way/ubuntu_pt \ # --ground-truth model-registry/ground-truth/ \ # --output model-registry/versions/vX.Y.Z/metrics.json ``` #### Step 7: Ubuntu — 创建版本目录 & 填写元数据 ```bash VERSION="vX.Y.Z" mkdir -p model-registry/versions/$VERSION # 写入 manifest.json(模板见下方 manifest.json 格式) vim model-registry/versions/$VERSION/manifest.json # 写入 metrics.json(汇总 Step 1 训练指标 + Step 6 对比结果 + 业务评测) vim model-registry/versions/$VERSION/metrics.json # 写入 notes.md(解决的问题、思路、已知限制) vim model-registry/versions/$VERSION/notes.md ``` **manifest.json 格式:** ```json { "version": "X.Y.Z", "tag": "vX.Y.Z", "commit": "", "date": "", "summary": "<一句话描述>", "artifacts": { "tracknet_pt": { "path": "weights/tracknet_v2_best.pt", "md5": "", "platform": "ubuntu", "generated_by": "TrackNetV2PyTorch/train.py" }, "rally_pt": { "path": "weights/rally_model.pt", "md5": "", "platform": "ubuntu", "generated_by": "RallyDetectModel/train_rally.py" }, "tracknet_coreml_fp16": { "path": "ModelConvert/outputs/TrackNetV2_fp16.mlpackage", "md5": "", "platform": "macos", "precision": "fp16", "source": "tracknet_pt", "generated_by": "ModelConvert/tracknet_coreml_convert.py" }, "tracknet_coreml_fp32": { "path": "ModelConvert/outputs/TrackNetV2_fp32.mlpackage", "md5": "", "platform": "macos", "precision": "fp32", "source": "tracknet_pt", "generated_by": "ModelConvert/tracknet_coreml_convert.py" }, "rally_coreml_fp16": { "path": "ModelConvert/outputs/RallyTCN.mlpackage", "md5": "", "platform": "macos", "precision": "fp16", "source": "rally_pt", "generated_by": "ModelConvert/rallytcn_coreml_convert.py" }, "tracknet_app": { "path": "iOSApps/Core/Models/TrackNetV2.mlpackage", "md5": "<复制到 Core/Models/ 后的 md5>", "platform": "ios/macos", "precision": "fp16", "source": "tracknet_coreml_fp16" }, "rally_app": { "path": "iOSApps/Core/Models/rally_v9.mlpackage", "md5": "<复制到 Core/Models/ 后的 md5>", "platform": "ios/macos", "precision": "fp16", "source": "rally_coreml_fp16" } } } ``` **metrics.json 格式:** ```json { "gt_version": "gt_v1", "date": "", "tracknet": { "training": { "val_loss": 0.0, "val_precision": 0.0, "val_recall": 0.0, "val_f1": 0.0 }, "business": { "visibility_accuracy": 0.0, "mean_pixel_error": 0.0, "rmse": 0.0, "num_gt_frames": 0, "note": "对比 ground-truth/tracker/ 逐帧标注" } }, "rally": { "training": { "val_accuracy": 0.0, "val_f1": 0.0 }, "business": { "num_gt_rallies": 0, "true_positives": 0, "false_positives": 0, "false_negatives": 0, "precision": 0.0, "recall": 0.0, "f1": 0.0, "note": "对比 ground-truth/rally/ 回合标注" } }, "cross_platform": { "pt_vs_coreml_fp16": { "tracknet_visibility_match": 0.0, "rally_segment_match": 0.0, "evaluated_by": "pipeline/diff_3way.py" }, "fp16_vs_fp32": { "tracknet_visibility_match": 0.0, "rally_segment_match": 0.0, "evaluated_by": "pipeline/diff_3way.py" } } } ``` #### Step 8: Ubuntu — 复制 App 模型 & 更新 registry.json ```bash # 将 CoreML fp16 产物复制到 App 打包目录 cp ModelConvert/outputs/TrackNetV2_fp16.mlpackage iOSApps/Core/Models/TrackNetV2.mlpackage cp ModelConvert/outputs/RallyTCN.mlpackage iOSApps/Core/Models/rally_v9.mlpackage # 计算 App 模型的 MD5 并写入 manifest.json find iOSApps/Core/Models/TrackNetV2.mlpackage -type f -exec md5sum {} \; | sort | md5sum find iOSApps/Core/Models/rally_v9.mlpackage -type f -exec md5sum {} \; | sort | md5sum # 同步 registry.json 到 Core SPM resource cp model-registry/registry.json iOSApps/Core/Sources/TennisTracker/Resources/registry.json ``` 更新 `model-registry/registry.json`,在 `history` 数组头部插入新版本条目: ```json { "version": "X.Y.Z", "date": "", "commit": "", "tag": "vX.Y.Z", "summary": "<一句话描述>" } ``` 同时更新 `latest`、`tracknet`、`rally` 字段为新版本号和新 MD5。 #### Step 9: Ubuntu — Git 打 tag 并提交 ```bash # 确保所有产物和元数据都已就绪 git status # 提交所有模型元数据 git add model-registry/versions/$VERSION/ \ model-registry/registry.json \ iOSApps/Core/Models/ \ iOSApps/Core/Sources/TennisTracker/Resources/registry.json git commit -m "release: $VERSION — <关键指标摘要>" # 打 tag(annotation 写关键指标) git tag -a $VERSION -m "TrackNet V2 + Rally TCN $VERSION 回合 F1=x.xxx 轨迹可见性=xx.x% PT vs CoreML 一致性=xx.x%" # 推送代码和 tag git push && git push --tags ``` ### 指标字段说明 | 字段 | 来源 | 说明 | |------|------|------| | `tracknet.training` | train.py 输出 | val_loss, val_precision, val_recall, val_f1 | | `tracknet.business` | 对比 ground-truth/tracker/ | visibility_accuracy, mean_pixel_error, rmse | | `rally.training` | train_rally.py 输出 | val_accuracy, val_f1 | | `rally.business` | 对比 ground-truth/rally/ | precision, recall, f1(TP/FP/FN vs 真值回合) | | `cross_platform` | diff_3way.py | PT vs CoreML FP16/FP32 逐帧一致性 | ### 真值数据格式 **ground-truth/tracker/{video_name}.csv** — 逐帧球坐标真值: ```csv frame_index,x,y,visibility 0,512.3,288.7,1.0 1,510.1,290.2,1.0 2,0.0,0.0,0.0 ``` **ground-truth/rally/{video_name}.json** — 回合段真值: ```json [ {"startFrame": 120, "endFrame": 450}, {"startFrame": 680, "endFrame": 1100} ] ``` ### 版本查询 ```bash # 列出所有版本 tag 及摘要 git tag -l 'v*' -n10 # 查看某个版本的完整指标 cat model-registry/versions/v1.0.0/metrics.json # App 端读取(Swift) ModelRegistry.load()?.latest // 当前版本号 ModelRegistry.load()?.tracknet.md5 // TrackNet 模型 MD5 ``` ## 数据 - `data/video/` — 测试视频 (.mp4) - `data/Rally_Labels/` — Ground-truth 回合标注 (JSON) - `weights/` — 模型权重 (.pt) - `model-registry/ground-truth/` — 业务评测真值数据