opensportslib 0.3.0.dev10__tar.gz → 0.3.0.dev12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. {opensportslib-0.3.0.dev10/opensportslib.egg-info → opensportslib-0.3.0.dev12}/PKG-INFO +8 -2
  2. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/README.md +7 -1
  3. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/apis/base_task_model.py +2 -1
  4. opensportslib-0.3.0.dev12/opensportslib/configs/localization/h5_header_distance.yaml +119 -0
  5. opensportslib-0.3.0.dev12/opensportslib/configs/localization/h5_header_skeleton.yaml +161 -0
  6. opensportslib-0.3.0.dev12/opensportslib/configs/localization/tracking_action_spotting.yaml +151 -0
  7. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/accessors.py +23 -2
  8. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/loader.py +53 -0
  9. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/trainer/localization_trainer.py +22 -29
  10. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/checkpoint.py +34 -17
  11. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/config.py +18 -1
  12. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/load_annotations.py +14 -0
  13. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/wandb.py +6 -1
  14. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/datasets/classification_dataset.py +8 -2
  15. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/datasets/localization_dataset.py +17 -0
  16. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/datasets/utils/h5_tracking.py +59 -0
  17. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/datasets/utils/tracking.py +16 -12
  18. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/metrics/localization_metric.py +69 -33
  19. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/rule_based.py +175 -20
  20. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12/opensportslib.egg-info}/PKG-INFO +8 -2
  21. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib.egg-info/SOURCES.txt +1 -5
  22. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/pyproject.toml +1 -1
  23. opensportslib-0.3.0.dev12/scripts/run_h5_header_rule_inference.py +869 -0
  24. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_h5_header_skeleton_spotter.py +174 -4
  25. opensportslib-0.3.0.dev10/opensportslib/configs/localization/h5_header_distance.yaml +0 -93
  26. opensportslib-0.3.0.dev10/opensportslib/configs/localization/h5_header_distance_angle.yaml +0 -93
  27. opensportslib-0.3.0.dev10/opensportslib/configs/localization/h5_header_distance_speed.yaml +0 -93
  28. opensportslib-0.3.0.dev10/opensportslib/configs/localization/h5_header_distance_speed_angle.yaml +0 -93
  29. opensportslib-0.3.0.dev10/opensportslib/configs/localization/h5_header_rule.yaml +0 -99
  30. opensportslib-0.3.0.dev10/opensportslib/configs/localization/h5_header_skeleton.yaml +0 -65
  31. opensportslib-0.3.0.dev10/scripts/run_h5_header_rule_inference.py +0 -65
  32. opensportslib-0.3.0.dev10/scripts/run_header_spotting_eval.py +0 -495
  33. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/LICENSE +0 -0
  34. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/LICENSE-COMMERCIAL +0 -0
  35. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/MANIFEST.in +0 -0
  36. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/examples/quickstart/basic_classification.py +0 -0
  37. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/examples/quickstart/basic_localization.py +0 -0
  38. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/examples/quickstart/basic_vqa.py +0 -0
  39. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/__init__.py +0 -0
  40. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/adaptation/__init__.py +0 -0
  41. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/adaptation/spotta.py +0 -0
  42. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/apis/__init__.py +0 -0
  43. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/apis/classification.py +0 -0
  44. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/apis/localization.py +0 -0
  45. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/apis/vqa.py +0 -0
  46. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/cli.py +0 -0
  47. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/classification/default.yaml +0 -0
  48. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/classification/sngar_frames.yaml +0 -0
  49. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/classification/sngar_tracking.yaml +0 -0
  50. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/classification/video.yaml +0 -0
  51. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/default.yaml +0 -0
  52. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/localization/calf_resnetpca512.yaml +0 -0
  53. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/localization/default.yaml +0 -0
  54. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/localization/e2e_spotta.yaml +0 -0
  55. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/localization/netvladpp_resnetpca512.yaml +0 -0
  56. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/localization/video_dali.yaml +0 -0
  57. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/localization/video_ocv.yaml +0 -0
  58. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/vqa/default.yaml +0 -0
  59. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/vqa/qwen.yaml +0 -0
  60. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/vqa/qwen3_vl_native.yaml +0 -0
  61. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/vqa/qwen_lora.yaml +0 -0
  62. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/vqa/qwen_sngar_frames.yaml +0 -0
  63. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/configs/vqa/xvars.yaml +0 -0
  64. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/__init__.py +0 -0
  65. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/__init__.py +0 -0
  66. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/conflicts.py +0 -0
  67. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/migrate.py +0 -0
  68. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/migrations/__init__.py +0 -0
  69. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/migrations/legacy_to_canonical.py +0 -0
  70. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/runtime_adapter.py +0 -0
  71. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/schema.py +0 -0
  72. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/schemas/__init__.py +0 -0
  73. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/schemas/schema_canonical.py +0 -0
  74. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/schemas/schema_legacy.py +0 -0
  75. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/config/validate.py +0 -0
  76. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/loss/__init__.py +0 -0
  77. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/loss/builder.py +0 -0
  78. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/loss/calf.py +0 -0
  79. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/loss/ce.py +0 -0
  80. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/loss/combine.py +0 -0
  81. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/loss/nll.py +0 -0
  82. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/optimizer/__init__.py +0 -0
  83. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/optimizer/builder.py +0 -0
  84. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/sampler/weighted_sampler.py +0 -0
  85. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/scheduler/__init__.py +0 -0
  86. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/scheduler/builder.py +0 -0
  87. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/trainer/__init__.py +0 -0
  88. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/trainer/classification_trainer.py +0 -0
  89. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/trainer/vqa_trainer.py +0 -0
  90. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/config_normalize.py +0 -0
  91. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/data.py +0 -0
  92. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/ddp.py +0 -0
  93. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/default_args.py +0 -0
  94. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/hf_runtime.py +0 -0
  95. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/lightning.py +0 -0
  96. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/seed.py +0 -0
  97. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/core/utils/video_processing.py +0 -0
  98. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/datasets/__init__.py +0 -0
  99. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/datasets/builder.py +0 -0
  100. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/datasets/utils/__init__.py +0 -0
  101. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/datasets/vqa_dataset.py +0 -0
  102. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/legacy_config/classification.yaml +0 -0
  103. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/legacy_config/localization-e2e-ocv.yaml +0 -0
  104. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/legacy_config/localization-json_calf_resnetpca512.yaml +0 -0
  105. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/legacy_config/localization-json_netvlad++_resnetpca512.yaml +0 -0
  106. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/legacy_config/localization.yaml +0 -0
  107. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/legacy_config/sngar-frames.yaml +0 -0
  108. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/legacy_config/sngar-tracking.yaml +0 -0
  109. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/metrics/classification_metric.py +0 -0
  110. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/metrics/vqa_metric.py +0 -0
  111. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/__init__.py +0 -0
  112. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/backbones/builder.py +0 -0
  113. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/contextaware.py +0 -0
  114. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/e2e.py +0 -0
  115. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/learnablepooling.py +0 -0
  116. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/qwen_vl_native.py +0 -0
  117. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/qwen_xvars.py +0 -0
  118. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/tracking.py +0 -0
  119. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/vars.py +0 -0
  120. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/video.py +0 -0
  121. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/video_chatgpt_compat.py +0 -0
  122. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/video_mae.py +0 -0
  123. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/base/xvars_videochatgpt.py +0 -0
  124. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/builder.py +0 -0
  125. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/heads/builder.py +0 -0
  126. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/neck/builder.py +0 -0
  127. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/common.py +0 -0
  128. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/impl/__init__.py +0 -0
  129. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/impl/asformer.py +0 -0
  130. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/impl/calf.py +0 -0
  131. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/impl/gsm.py +0 -0
  132. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/impl/gtad.py +0 -0
  133. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/impl/tsm.py +0 -0
  134. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/litebase.py +0 -0
  135. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/modules.py +0 -0
  136. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/shift.py +0 -0
  137. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/utils.py +0 -0
  138. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/vqa_prediction_priors.py +0 -0
  139. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/vqa_prompting.py +0 -0
  140. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/models/utils/xvars_clip_index.py +0 -0
  141. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/setup/setup.py +0 -0
  142. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/tools/__init__.py +0 -0
  143. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/tools/_common.py +0 -0
  144. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/tools/hf_transfer.py +0 -0
  145. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/tools/osl_json_to_parquet.py +0 -0
  146. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib/tools/parquet_to_osl_json.py +0 -0
  147. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib.egg-info/dependency_links.txt +0 -0
  148. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib.egg-info/entry_points.txt +0 -0
  149. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib.egg-info/requires.txt +0 -0
  150. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/opensportslib.egg-info/top_level.txt +0 -0
  151. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/setup.cfg +0 -0
  152. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/conftest.py +0 -0
  153. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_classification_dataset_paths.py +0 -0
  154. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_classification_trainer_dataloader.py +0 -0
  155. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_config_architecture.py +0 -0
  156. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_config_split_override_sync.py +0 -0
  157. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_config_utils_smoke.py +0 -0
  158. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_conversion_tools.py +0 -0
  159. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_extract_xvars_features.py +0 -0
  160. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_h5_header_rule_spotter.py +0 -0
  161. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_h5_tracking_dataset.py +0 -0
  162. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_hf_transfer_tools.py +0 -0
  163. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_localization_dali_filenames.py +0 -0
  164. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_localization_hf_backend_override.py +0 -0
  165. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_localization_intervals.py +0 -0
  166. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_package_smoke.py +0 -0
  167. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_pretrained_config_merge_policy.py +0 -0
  168. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_public_apis_smoke.py +0 -0
  169. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_setup_cli.py +0 -0
  170. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_spotta_e2e.py +0 -0
  171. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_subset_train_infer_integration.py +0 -0
  172. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_task_model_api_contract.py +0 -0
  173. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_vqa_api.py +0 -0
  174. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_vqa_metrics_semantic.py +0 -0
  175. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_vqa_qwen_xvars.py +0 -0
  176. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_vqa_training_lora.py +0 -0
  177. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tests/test_vqa_xvars_videochatgpt.py +0 -0
  178. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/convert/build_sn_vqa_2026_vqa.py +0 -0
  179. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/convert/build_soccernet_gar.py +0 -0
  180. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
  181. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/convert/build_soccernet_gar_vqa.py +0 -0
  182. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/convert/build_xvars_indexes.py +0 -0
  183. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/convert/extract_xvars_clip_features.py +0 -0
  184. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
  185. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
  186. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/download/download_hf_repo.py +0 -0
  187. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/download/download_osl_hf.py +0 -0
  188. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/download/upload_osl_hf.py +0 -0
  189. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/training/classification.py +0 -0
  190. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/training/localization.py +0 -0
  191. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/training/vqa.py +0 -0
  192. {opensportslib-0.3.0.dev10 → opensportslib-0.3.0.dev12}/tools/upload/upload_model_hf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensportslib
3
- Version: 0.3.0.dev10
3
+ Version: 0.3.0.dev12
4
4
  Summary: OpenSportsLib is the professional library, designed for advanced video understanding in sports. It provides state-of-the-art tools for action recognition, spotting, retrieval, and captioning, making it ideal for researchers, analysts, and developers working with sports video data.
5
5
  Author: Jeet Vora
6
6
  Requires-Python: >=3.12
@@ -76,7 +76,7 @@ OpenSportsLib is designed for **researchers, ML engineers, and sports analytics
76
76
  ### Create conda env
77
77
 
78
78
  ```bash
79
- conda create -n osl python=3.12 pip
79
+ conda create -n osl python=3.12 pip -y
80
80
  conda activate osl
81
81
  ```
82
82
 
@@ -92,6 +92,12 @@ pip install opensportslib
92
92
  pip install --pre opensportslib
93
93
  ```
94
94
 
95
+ ### Source development version
96
+
97
+ ```bash
98
+ pip install -e .
99
+ ```
100
+
95
101
  ### Setup Environment (PyTorch, CUDA aware & Optional Dependencies)
96
102
  ```bash
97
103
  # Install PyTorch (CPU/GPU auto-detected)
@@ -40,7 +40,7 @@ OpenSportsLib is designed for **researchers, ML engineers, and sports analytics
40
40
  ### Create conda env
41
41
 
42
42
  ```bash
43
- conda create -n osl python=3.12 pip
43
+ conda create -n osl python=3.12 pip -y
44
44
  conda activate osl
45
45
  ```
46
46
 
@@ -56,6 +56,12 @@ pip install opensportslib
56
56
  pip install --pre opensportslib
57
57
  ```
58
58
 
59
+ ### Source development version
60
+
61
+ ```bash
62
+ pip install -e .
63
+ ```
64
+
59
65
  ### Setup Environment (PyTorch, CUDA aware & Optional Dependencies)
60
66
  ```bash
61
67
  # Install PyTorch (CPU/GPU auto-detected)
@@ -14,6 +14,7 @@ from opensportslib.core.utils.config import (
14
14
  expand,
15
15
  load_config_omega,
16
16
  fetch_and_merge_config_from_HF,
17
+ resolve_config_path,
17
18
  resolve_inference_class_metadata,
18
19
  )
19
20
 
@@ -27,7 +28,7 @@ class BaseTaskModel(ABC):
27
28
  if config is None:
28
29
  raise ValueError("config path is required")
29
30
 
30
- self.config_path = expand(config)
31
+ self.config_path = resolve_config_path(config)
31
32
  self.config = load_config_omega(self.config_path)
32
33
  self.last_loaded_weights = None
33
34
  self.best_checkpoint = None
@@ -0,0 +1,119 @@
1
+ # Distance-family header spotting.
2
+ #
3
+ # One config for all four distance variants. Pick the variant on the `name:`
4
+ # line under MODEL.components.rule.source, and tune the thresholds under
5
+ # `params:`. Anything left out of `params:` falls back to
6
+ # DEFAULT_HEADER_RULE_PARAMS in opensportslib/models/base/rule_based.py.
7
+ #
8
+ # The skeleton family has its own config, h5_header_skeleton.yaml.
9
+
10
+ TASK: localization
11
+ VERSION: 2
12
+
13
+ SYSTEM:
14
+ paths:
15
+ work_dir: ./outputs/header_spotting_distance
16
+ device: cpu
17
+ gpu:
18
+ count: 0
19
+
20
+ DATA:
21
+ common:
22
+ dataset_name: h5_headers
23
+ # --- CHANGE THESE to point at your tracking data -------------------------
24
+ # data_root and source_path are the directory the manifest's relative paths
25
+ # resolve against. annotation_path is a manifest listing the joints/ball h5
26
+ # pairs to scan; generate one with --combined, or write your own. See
27
+ # docs/headers/README.md.
28
+ data_root: /home/giancos/FIFA_data
29
+ classes:
30
+ - header
31
+ splits:
32
+ test:
33
+ type: H5OSLJsonSpotting
34
+ annotation_path: ./outputs/header_spotting/manifest_all.json
35
+ source_path: /home/giancos/FIFA_data
36
+ dataloader:
37
+ batch_size: 1
38
+ shuffle: false
39
+ num_workers: 0
40
+ pin_memory: false
41
+ inputs:
42
+ tracking:
43
+ modality: player_joints_h5
44
+ representation: raw
45
+ source:
46
+ format: h5
47
+ sampling: {}
48
+ transform: {}
49
+ augmentations: {}
50
+ params: {}
51
+
52
+ MODEL:
53
+ metadata:
54
+ family: RuleBased
55
+ runner:
56
+ type: runner_h5_header_rule
57
+ components:
58
+ rule:
59
+ kind: algorithm
60
+ source:
61
+ provider: opensportslib
62
+ registry: rule_based
63
+ # --- CHANGE THIS to switch variant -------------------------------
64
+ # h5_header_distance no check on the ball path
65
+ # h5_header_distance_angle path must bend by 25 degrees
66
+ # h5_header_distance_speed speed must change by 25 per cent
67
+ # h5_header_distance_speed_angle both
68
+ # The name alone selects the trajectory mode. Setting
69
+ # trajectory_change_mode below has no effect: HEADER_RULE_VARIANTS is
70
+ # applied after these params and overwrites it.
71
+ name: h5_header_distance
72
+ params:
73
+ label: header
74
+ head_name: action
75
+
76
+ # --- contact ------------------------------------------------------
77
+ # A detection needs distance < distance_threshold_m AND confidence >
78
+ # min_confidence, where confidence is 1 - distance/threshold. The two
79
+ # multiply: 0.5 and 0.5 mean the ball must come within 25 cm.
80
+ distance_threshold_m: 0.5
81
+ min_confidence: 0.5
82
+ # Which joints count as the head. Trim to [nose] to see how much the
83
+ # others contribute.
84
+ head_joints: [nose, neck, l_eye, r_eye, l_ear, r_ear]
85
+ # How stale a ball sample may be when matched to a joint row by time.
86
+ ball_tolerance_ms: 60
87
+
88
+ # --- trajectory, used by the angle and speed variants --------------
89
+ # Vectors are built from a ball sample this far before and after the
90
+ # contact, then compared for bend and for speed change.
91
+ trajectory_pre_window_ms: 200
92
+ trajectory_post_window_ms: 200
93
+ trajectory_min_angle_deg: 25.0
94
+ trajectory_min_speed_delta_ratio: 0.25
95
+ # Below this the ball has barely moved and the test is skipped.
96
+ trajectory_min_vector_norm_m: 0.05
97
+ trajectory_use_xy_only: false
98
+
99
+ # --- throw-ins ----------------------------------------------------
100
+ # Contacts nearer the touchline than sideline_exclusion_m are dropped.
101
+ sideline_filter_enabled: true
102
+ pitch_half_width_m: 50.0
103
+ sideline_exclusion_m: 1.0
104
+
105
+ # --- de-duplication -----------------------------------------------
106
+ # One detection per window, keeping the most confident.
107
+ nms_window_ms: 1000
108
+
109
+ # --- output --------------------------------------------------------
110
+ # Diagnostics carry the contact distance, joint, player and the
111
+ # measured trajectory values on every event.
112
+ include_diagnostics: true
113
+ topology: []
114
+
115
+ TRAIN:
116
+ trainer:
117
+ type: trainer_rule_based
118
+ execution:
119
+ enabled: false
@@ -0,0 +1,161 @@
1
+ # Skeleton-family header spotting.
2
+ #
3
+ # One config for both skeleton variants. Pick the variant on the `name:` line
4
+ # under MODEL.components.rule.source, and tune the thresholds under `params:`.
5
+ # Anything left out falls back to DEFAULT_SKELETON_RULE_PARAMS in
6
+ # opensportslib/models/base/rule_based.py.
7
+ #
8
+ # The numbered checks below match the sequence in docs/headers/README.md. The
9
+ # measured effects come from an 82-configuration sweep on the 2022 final,
10
+ # recorded in header_skeleton_sweep.xlsx; each is the effect of changing that
11
+ # one setting away from the strict defaults.
12
+
13
+ TASK: localization
14
+ VERSION: 2
15
+
16
+ SYSTEM:
17
+ paths:
18
+ work_dir: ./outputs/header_spotting_skeleton
19
+ device: cpu
20
+ gpu:
21
+ count: 0
22
+
23
+ DATA:
24
+ common:
25
+ dataset_name: h5_headers
26
+ # --- CHANGE THESE to point at your tracking data -------------------------
27
+ # data_root and source_path are the directory the manifest's relative paths
28
+ # resolve against. annotation_path is a manifest listing the joints/ball h5
29
+ # pairs to scan; generate one with --combined, or write your own. See
30
+ # docs/headers/README.md.
31
+ data_root: /home/giancos/FIFA_data
32
+ classes:
33
+ - header
34
+ splits:
35
+ test:
36
+ type: H5OSLJsonSpotting
37
+ annotation_path: ./outputs/header_spotting/manifest_all.json
38
+ source_path: /home/giancos/FIFA_data
39
+ dataloader:
40
+ batch_size: 1
41
+ shuffle: false
42
+ num_workers: 0
43
+ pin_memory: false
44
+ inputs:
45
+ tracking:
46
+ modality: player_joints_h5
47
+ representation: raw
48
+ source:
49
+ format: h5
50
+ sampling: {}
51
+ transform: {}
52
+ augmentations: {}
53
+ params: {}
54
+
55
+ MODEL:
56
+ metadata:
57
+ family: RuleBased
58
+ runner:
59
+ type: runner_h5_header_rule
60
+ components:
61
+ rule:
62
+ kind: algorithm
63
+ source:
64
+ provider: opensportslib
65
+ registry: rule_based
66
+ # --- CHANGE THIS to switch variant -------------------------------
67
+ # h5_header_skeleton 91.4% recall, 86.5% precision
68
+ # h5_header_skeleton_recall 97.1% recall, 69.4% precision
69
+ # h5_header_skeleton_max_recall 100% recall, 61.4% precision
70
+ #
71
+ # With either recall variant, SKELETON_RULE_VARIANTS is applied
72
+ # after these params and overwrites twelve of them: head_joints, the
73
+ # five trajectory gates, both height bounds, ankle_height_max_m,
74
+ # dwell_max_frames and nms_window_frames. Setting those below has no
75
+ # effect on that variant. Everything else still applies to both.
76
+ name: h5_header_skeleton
77
+ params:
78
+ label: header
79
+ head_name: action
80
+
81
+ # --- check 4: contact ---------------------------------------------
82
+ # Distance from the ball to the nearest tracked head joint. Each joint
83
+ # is measured separately and the smallest wins; no midpoint is used.
84
+ # Measured: 0.5 gains 5 points of precision and 4.3 of mAP for 2.8 of
85
+ # recall, and is the single best change to these defaults. 0.25
86
+ # collapses recall to 49%.
87
+ head_ball_distance_max_m: 0.4
88
+ # Trim to [nose] to see how much the others contribute. All six help
89
+ # the recall variant, where 77% of detections match on something other
90
+ # than the nose, but cost the strict variant about 4 points of recall.
91
+ head_joints: [nose]
92
+
93
+ # --- check 1: ball height ------------------------------------------
94
+ # Measured: removing this band costs 4.6 points of precision. Worth
95
+ # keeping. Narrowing to 1.5-2.5 costs 10 points of recall.
96
+ ball_height_min_m: 1.3
97
+ ball_height_max_m: 3.0
98
+ # Sentinel marking an untracked coordinate.
99
+ invalid_value: -1.0
100
+
101
+ # --- checks 2 and 3: what the ball did ------------------------------
102
+ # Velocity comes from adjacent frames, acceleration from two frames
103
+ # either side.
104
+ #
105
+ # Measured: only the bend test earns its place. 25 degrees gains about
106
+ # 2 points of precision over 10. The other four are dead weight on this
107
+ # data: velocity_change actively costs recall and precision, while
108
+ # velocity_mag and accel_z_change produce byte-identical output whether
109
+ # on or off, meaning they never fire.
110
+ angle_change_min_deg: 10.0
111
+ velocity_change_min_mps: 2.0
112
+ velocity_mag_min_mps: 1.0
113
+ accel_z_change_min_mps2: 8.0
114
+ incoming_speed_min_mps: 4.0
115
+
116
+ # --- checks 5, 6, 7: the player -------------------------------------
117
+ # Facing is the dot product of the shoulder-line normal with the
118
+ # direction to the ball. Measured: inert at -0.5, it rejects nothing.
119
+ # Raising it to 0.0 costs 18 points of recall, so it is a blunt tool.
120
+ facing_dot_min: -0.5
121
+ # A hand nearer the ball than the head means an arm or a keeper's grab.
122
+ # Measured: worth 2.8 points of precision and free in recall.
123
+ hand_check_enabled: true
124
+ # Both ankles must be below this. Measured: harmful here, removing it
125
+ # improves recall, precision and F1 together.
126
+ ankle_height_max_m: 1.2
127
+
128
+ # --- check 8: dwell --------------------------------------------------
129
+ # Counts frames within dwell_window_frames either side where the head is
130
+ # within the dwell radius. A ball that lingers was carried, not headed.
131
+ # Measured: the strongest gate. Removing it costs 3.8 points of
132
+ # precision; tightening to 3 reaches 94.6% precision at 67% recall.
133
+ dwell_max_frames: 5
134
+ dwell_window_frames: 3
135
+ # The dwell radius is head_ball_distance_max_m * dwell_distance_factor
136
+ # unless dwell_distance_m is set. Set it when widening the contact
137
+ # threshold, or the dwell filter silently tightens with it.
138
+ dwell_distance_factor: 1.2
139
+ dwell_distance_m: null
140
+
141
+ # --- de-duplication --------------------------------------------------
142
+ # One detection per window, keeping the closest contact. Measured: 40
143
+ # gains 1.5 points of precision and 1.3 of mAP; 60 gains 4.3 of
144
+ # precision for 1.9 of recall.
145
+ nms_window_frames: 25
146
+ fps: 50.0
147
+
148
+ # --- output ----------------------------------------------------------
149
+ # Diagnostics carry the contact distance, which head joint matched, the
150
+ # player, the ball height, the dwell count and the trajectory values.
151
+ include_diagnostics: true
152
+ # Added to every position_ms. Leave at 0 unless correcting a known
153
+ # offset between the tracking and the clock you report against.
154
+ position_offset_ms: 0.0
155
+ topology: []
156
+
157
+ TRAIN:
158
+ trainer:
159
+ type: trainer_rule_based
160
+ execution:
161
+ enabled: false
@@ -0,0 +1,151 @@
1
+ SYSTEM:
2
+ paths:
3
+ save_dir: ./checkpoints_as_tracking
4
+ gpu:
5
+ count: 1
6
+
7
+ DATA:
8
+ common:
9
+ dataset_name: SoccerNet-GAR-Spotting-Tracking
10
+ data_root: /home/karkid/PFF/sngar-spotting-tracking-dedup
11
+ classes:
12
+ - PASS
13
+ - HEADER
14
+ - HIGH PASS
15
+ - OUT
16
+ - CROSS
17
+ - THROW IN
18
+ - SHOT
19
+ - PLAYER SUCCESSFUL TACKLE
20
+ - FREE KICK
21
+ - GOAL
22
+ splits:
23
+ train:
24
+ type: TrackingActionSpot
25
+ annotation_path: ${DATA.common.data_root}/annotations_train.json
26
+ source_path: ${DATA.common.data_root}
27
+ dataloader:
28
+ batch_size: 4
29
+ shuffle: true
30
+ num_workers: 16 # graph build is CPU bound
31
+ pin_memory: true
32
+ valid:
33
+ type: TrackingActionSpot
34
+ annotation_path: ${DATA.common.data_root}/annotations_valid.json
35
+ source_path: ${DATA.common.data_root}
36
+ dataloader:
37
+ batch_size: 4
38
+ shuffle: false
39
+ num_workers: 16
40
+ pin_memory: true
41
+ valid_data_frames:
42
+ type: TrackingActionSpotVideo
43
+ annotation_path: ${DATA.common.splits.valid.annotation_path}
44
+ source_path: ${DATA.common.splits.valid.source_path}
45
+ overlap_len: 0
46
+ dataloader:
47
+ batch_size: 1
48
+ shuffle: false
49
+ num_workers: 2
50
+ pin_memory: false
51
+ test:
52
+ type: TrackingActionSpotVideo
53
+ annotation_path: ${DATA.common.data_root}/annotations_test.json
54
+ source_path: ${DATA.common.data_root}
55
+ results: results_spotting_test
56
+ metric: tight
57
+ nms_window: 2
58
+ overlap_len: 270 # 90% of clip_len
59
+ dataloader:
60
+ batch_size: 1
61
+ shuffle: false
62
+ num_workers: 2
63
+ pin_memory: false
64
+
65
+ inputs:
66
+ video:
67
+ modality: tracking_parquet
68
+ representation: raw
69
+ source:
70
+ format: parquet
71
+ sampling:
72
+ epoch_num_frames: 1500000 # 5000 clips/epoch
73
+ clip_len: 300
74
+ extract_fps: 5
75
+ transform:
76
+ normalize: true # x, y, dx, dy, z by the pitch constants below
77
+ augmentations: # train only; the dataset gates on is_eval
78
+ vertical_flip: true
79
+ horizontal_flip: true
80
+ team_flip: true
81
+ params:
82
+ dilate_len: 1
83
+ mixup: false # undefined on graphs; overrides the RGB default
84
+ objects:
85
+ num_objects: 23
86
+ feature_dim: 8
87
+ pitch_half_length: 85.0
88
+ pitch_half_width: 50.0
89
+ max_displacement: 110.0
90
+ max_ball_height: 30.0
91
+
92
+ MODEL:
93
+ metadata:
94
+ family: E2E
95
+ runner:
96
+ type: runner_e2e
97
+ components:
98
+ video_encoder:
99
+ kind: encoder
100
+ source:
101
+ provider: opensportslib
102
+ registry: backbone
103
+ name: graph_conv_seq # per-frame embeddings, not a pooled clip vector
104
+ params:
105
+ # gen GENConv softmax aggregation, learnable t and p
106
+ # gat GATv2Conv four attention heads
107
+ # sageconv SAGEConv mean, max and std aggregation
108
+ # gin GINConv sum aggregation over a two-layer MLP
109
+ # edgeconv EdgeConv max aggregation over node pairs
110
+ # graphconv GraphConv plain add
111
+ encoder: graphconv
112
+ hidden_dim: 64
113
+ num_layers: 20
114
+ dropout: 0.1
115
+ edge_type: positional
116
+ k: 8
117
+ radius: 15.0
118
+ task_head:
119
+ kind: head
120
+ source:
121
+ provider: opensportslib
122
+ registry: head
123
+ name: gru
124
+
125
+ TRAIN:
126
+ trainer:
127
+ type: trainer_e2e
128
+ epochs: 100
129
+ criterion:
130
+ type: CrossEntropyLoss
131
+ optimizer:
132
+ type: AdamWithScaler
133
+ lr: 0.001
134
+ scheduler:
135
+ type: ChainedSchedulerE2E
136
+ acc_grad_iter: 1
137
+ num_epochs: ${TRAIN.epochs}
138
+ warm_up_epochs: 5
139
+ execution:
140
+ enabled: true
141
+ multi_gpu: false
142
+ acc_grad_iter: 1
143
+ base_num_valid_epochs: 30
144
+ start_valid_epoch: 4
145
+ valid_map_every: 10
146
+ criterion_valid: map
147
+ selection:
148
+ monitor: valid_loss
149
+ mode: min
150
+ checkpoint:
151
+ save_best: true
@@ -182,11 +182,32 @@ def get_split_source_path(cfg: Any, split: str) -> str | None:
182
182
  return getattr(split_cfg, "source_path", None)
183
183
 
184
184
 
185
+ def classes_to_ordered_list(classes: Any) -> list[str]:
186
+ """Normalise a classes spec to a list ordered by class index.
187
+
188
+ Classes appear either as a list (already in index order) or as a
189
+ ``{name: index}`` mapping - the form written into a run's saved
190
+ config.yaml. ``list(mapping)`` returns keys in *insertion* order, and a
191
+ mapping round-tripped through YAML comes back alphabetically sorted, so
192
+ that would silently permute the class<->index mapping when a checkpoint's
193
+ config is reloaded for inference. Always order a mapping by its indices.
194
+ """
195
+ if classes is None:
196
+ return []
197
+ if OmegaConf is not None and OmegaConf.is_config(classes):
198
+ classes = OmegaConf.to_container(classes, resolve=True)
199
+ if isinstance(classes, dict):
200
+ try:
201
+ return [name for name, _ in sorted(classes.items(), key=lambda kv: int(kv[1]))]
202
+ except (TypeError, ValueError):
203
+ return list(classes)
204
+ return list(classes)
205
+
206
+
185
207
  def get_data_classes(cfg: Any) -> list[str]:
186
208
  data = _as_dict(getattr(cfg, "DATA", None))
187
209
  common = _as_dict(data.get("common"))
188
- classes = common.get("classes", [])
189
- return list(classes) if classes is not None else []
210
+ return classes_to_ordered_list(common.get("classes", []))
190
211
 
191
212
 
192
213
  def get_data_num_classes(cfg: Any, default: int = 0) -> int:
@@ -148,11 +148,64 @@ def _dali_available() -> bool:
148
148
  return importlib.util.find_spec("nvidia.dali") is not None
149
149
 
150
150
 
151
+ # DALI decodes video; any other input modality (tracking parquets, features)
152
+ # has no DALI pipeline and must never be switched onto that backend.
153
+ _DALI_CAPABLE_MODALITIES = {"video", "rgb", "video_mp4"}
154
+
155
+
156
+ def _inputs_are_dali_capable(payload: dict[str, Any]) -> bool:
157
+ """Whether this config's inputs can be read by DALI at all.
158
+
159
+ A missing/unrecognised modality is treated as video so existing RGB
160
+ configs keep their behaviour; only an explicitly non-video modality
161
+ (e.g. tracking_parquet) opts out.
162
+ """
163
+ data = payload.get("DATA", {})
164
+ if not isinstance(data, dict):
165
+ return True
166
+ inputs = data.get("inputs", {})
167
+ if not isinstance(inputs, dict) or not inputs:
168
+ return True
169
+
170
+ for spec in inputs.values():
171
+ if not isinstance(spec, dict):
172
+ continue
173
+ modality = spec.get("modality")
174
+ if modality is not None and str(modality).lower() not in _DALI_CAPABLE_MODALITIES:
175
+ return False
176
+ return True
177
+
178
+
179
+ def _declares_opencv_split_types(payload: dict[str, Any]) -> bool:
180
+ """Whether the config explicitly asks for the OpenCV video datasets."""
181
+ data = payload.get("DATA", {})
182
+ common = data.get("common", {}) if isinstance(data, dict) else {}
183
+ splits = common.get("splits", {}) if isinstance(common, dict) else {}
184
+ if not isinstance(splits, dict):
185
+ return False
186
+ opencv_types = set(_CPU_OPENCV_SPLIT_TYPES.values())
187
+ return any(
188
+ isinstance(cfg, dict) and cfg.get("type") in opencv_types
189
+ for cfg in splits.values()
190
+ )
191
+
192
+
151
193
  def _preferred_loader_backend(payload: dict[str, Any]) -> str | None:
152
194
  system = payload.get("SYSTEM", {})
153
195
  if not isinstance(system, dict):
154
196
  return None
155
197
 
198
+ # Selecting DALI for non-video data yields tracking/feature datasets driven
199
+ # through the DALI training branch, which fails with KeyError: 'frame'.
200
+ if not _inputs_are_dali_capable(payload):
201
+ return "opencv"
202
+
203
+ # Likewise honour split types that explicitly name the OpenCV datasets:
204
+ # forcing DALI leaves those types in place (only DALI->OpenCV is remapped
205
+ # below), producing the same mismatched-branch failure.
206
+ if _declares_opencv_split_types(payload):
207
+ return "opencv"
208
+
156
209
  mode = str(system.get("device", "auto")).lower()
157
210
  if mode == "cpu":
158
211
  return "opencv"
@@ -399,15 +399,21 @@ class Trainer_e2e(Trainer):
399
399
 
400
400
  # ---------------- W&B LOG ----------------
401
401
  if wandb.run is not None:
402
- wandb.log({
402
+ payload = {
403
403
  "epoch": epoch + 1,
404
404
  "train/loss": train_loss,
405
405
  "valid/loss": valid_loss,
406
- "valid/mAP": valid_mAP,
407
406
  "lr": self.optimizer.param_groups[0]["lr"],
408
407
  "best/mAP": self.best_criterion_valid if self.criterion_valid == "map" else None,
409
408
  "best/loss": self.best_criterion_valid if self.criterion_valid == "loss" else None,
410
- })
409
+ }
410
+ # Whole-match mAP only runs every valid_map_every epochs.
411
+ # Logging a placeholder 0 on the other epochs drew a sawtooth
412
+ # collapsing to zero between real measurements; omit the key
413
+ # instead so the chart connects the points that exist.
414
+ if valid_mAP:
415
+ payload["valid/mAP"] = valid_mAP
416
+ wandb.log(payload)
411
417
 
412
418
  if self.save_dir is not None:
413
419
  os.makedirs(self.save_dir, exist_ok=True)
@@ -883,6 +889,9 @@ class Evaluator:
883
889
  targets_numpy = []
884
890
  detections_numpy = []
885
891
  closests_numpy = []
892
+ # Rate the dense vectors below end up sampled at; the mAP tolerances
893
+ # are in seconds and must be converted with this same rate.
894
+ eval_framerate = self.extract_fps
886
895
 
887
896
  # ==================================================
888
897
  # LOOP
@@ -975,32 +984,15 @@ class Evaluator:
975
984
  fps = pred_data_local.get("fps", self.extract_fps)
976
985
 
977
986
  # ---------------- VECTORS ----------------
978
- if gt_is_v2:
979
- frame_candidates = [1]
980
- if game["end_time_ms"] is not None:
981
- frame_candidates.append(
982
- int(
983
- math.ceil(
984
- (game["end_time_ms"] - game["start_time_ms"])
985
- / 1000
986
- * fps
987
- )
988
- )
989
- )
990
- for event in labels:
991
- frame_candidates.append(
992
- int(fps * (event["position"] / 1000)) + 1
993
- )
994
- for event in predictions:
995
- if event.get("frame") is not None:
996
- frame_candidates.append(int(event["frame"]) + 1)
997
- elif event.get("position") is not None:
998
- frame_candidates.append(
999
- int(fps * (int(event["position"]) / 1000)) + 1
1000
- )
1001
- vector_size = max(frame_candidates)
1002
- else:
1003
- vector_size = game.get("num_frames")
987
+ # Size the dense vectors from the actual content instead of the
988
+ # 90-minute default: a match clock can run well past 90 min
989
+ # (kick-off offset, stoppage, half-time gap) and anything beyond
990
+ # the cap is clamped onto the final bin, silently merging events.
991
+ positions_ms = [a["position"] for a in labels] + [
992
+ p["position"] for p in predictions
993
+ ]
994
+ vector_size = int(fps * (max(positions_ms) / 1000)) + 2 if positions_ms else None
995
+ eval_framerate = fps
1004
996
 
1005
997
  dense_labels = label2vector(
1006
998
  labels,
@@ -1033,6 +1025,7 @@ class Evaluator:
1033
1025
  detections_numpy,
1034
1026
  closests_numpy,
1035
1027
  INVERSE_EVENT_DICTIONARY,
1028
+ framerate=eval_framerate,
1036
1029
  )
1037
1030
  else:
1038
1031
  logging.warning("No predictions found.")