opensportslib 0.3.1.dev18__tar.gz → 0.3.1.dev19__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.
- {opensportslib-0.3.1.dev18/opensportslib.egg-info → opensportslib-0.3.1.dev19}/PKG-INFO +40 -1
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/README.md +38 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/apis/classification.py +13 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/apis/localization.py +10 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/accessors.py +14 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/load_annotations.py +9 -2
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/video_processing.py +7 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/datasets/classification_dataset.py +20 -0
- opensportslib-0.3.1.dev19/opensportslib/datasets/hf_json.py +169 -0
- opensportslib-0.3.1.dev19/opensportslib/datasets/hf_tracking.py +297 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/datasets/localization_dataset.py +9 -15
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19/opensportslib.egg-info}/PKG-INFO +40 -1
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib.egg-info/SOURCES.txt +6 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib.egg-info/requires.txt +1 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/pyproject.toml +2 -2
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/conftest.py +2 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/contracts/test_dependencies.py +2 -1
- opensportslib-0.3.1.dev19/tests/unit/data/test_hf_json_spotting.py +233 -0
- opensportslib-0.3.1.dev19/tests/unit/data/test_hf_json_tracking_spotting.py +105 -0
- opensportslib-0.3.1.dev19/tests/unit/data/test_hf_tracking_dataset.py +246 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/data/test_localization_intervals.py +66 -0
- opensportslib-0.3.1.dev19/tools/train/train_config.py +32 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/LICENSE +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/LICENSE-COMMERCIAL +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/MANIFEST.in +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/examples/quickstart/basic_classification.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/examples/quickstart/basic_localization.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/examples/quickstart/basic_vqa.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/adaptation/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/adaptation/spotta.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/apis/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/apis/base_task_model.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/apis/config.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/apis/configuration.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/apis/vqa.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/cli.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/classification/default.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/classification/sngar_frames.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/classification/sngar_tracking.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/classification/video.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/default.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/localization/calf_resnetpca512.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/localization/default.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/localization/e2e_spotta.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/localization/h5_header_distance.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/localization/h5_header_skeleton.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/localization/netvladpp_resnetpca512.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/localization/tracking_action_spotting.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/localization/video_dali.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/localization/video_ocv.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/vqa/default.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/vqa/qwen.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/vqa/qwen3_vl_native.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/vqa/qwen_lora.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/vqa/qwen_sngar_frames.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/configs/vqa/xvars.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/conflicts.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/editable.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/loader.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/migrate.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/migrations/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/migrations/legacy_to_canonical.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/rule_variants.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/runtime_adapter.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/schema.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/schemas/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/schemas/schema_canonical.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/schemas/schema_legacy.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/validate.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/loss/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/loss/builder.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/loss/calf.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/loss/ce.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/loss/combine.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/loss/nll.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/optimizer/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/optimizer/builder.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/sampler/weighted_sampler.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/scheduler/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/scheduler/builder.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/trainer/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/trainer/classification_trainer.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/trainer/localization_trainer.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/trainer/vqa_trainer.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/checkpoint.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/config.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/config_normalize.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/data.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/ddp.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/default_args.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/direct_video.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/hf_runtime.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/lightning.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/seed.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/wandb.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/datasets/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/datasets/builder.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/datasets/utils/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/datasets/utils/h5_tracking.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/datasets/utils/tracking.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/datasets/vqa_dataset.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/legacy_config/classification.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/legacy_config/localization-e2e-ocv.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/legacy_config/localization-json_calf_resnetpca512.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/legacy_config/localization-json_netvlad++_resnetpca512.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/legacy_config/localization.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/legacy_config/sngar-frames.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/legacy_config/sngar-tracking.yaml +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/metrics/classification_metric.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/metrics/localization_metric.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/metrics/vqa_metric.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/backbones/builder.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/contextaware.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/e2e.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/learnablepooling.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/qwen_vl_native.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/qwen_xvars.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/rule_based.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/tracking.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/vars.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/video.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/video_chatgpt_compat.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/video_mae.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/base/xvars_videochatgpt.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/builder.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/heads/builder.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/neck/builder.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/common.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/impl/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/impl/asformer.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/impl/calf.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/impl/gsm.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/impl/gtad.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/impl/tsm.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/litebase.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/modules.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/shift.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/utils.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/vqa_prediction_priors.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/vqa_prompting.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/models/utils/xvars_clip_index.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/remote_registry.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/setup/setup.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/tools/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/tools/_common.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/tools/hf_transfer.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/tools/osl_json_to_parquet.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/tools/parquet_to_osl_json.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib.egg-info/dependency_links.txt +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib.egg-info/entry_points.txt +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib.egg-info/top_level.txt +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/scripts/redact_test_stream.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/scripts/release_versions.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/scripts/run_h5_header_rule_inference.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/scripts/summarize_test_report.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/setup.cfg +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/helpers/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/helpers/configs.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/helpers/pipeline_stubs.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/integration/classification/test_pipeline.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/integration/localization/test_pipeline.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/integration/localization/test_spotta.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/integration/vqa/test_pipeline.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/release/__init__.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/release/_release_common.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/release/test_00_environment.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/release/test_classification_release.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/release/test_localization_release.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/release/test_vqa_release.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/smoke/test_cli.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/smoke/test_config_utils.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/smoke/test_environment.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/smoke/test_package.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/smoke/test_public_apis.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/api/test_remote_registry.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/api/test_remote_sessions.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/api/test_server_jobs.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/api/test_server_registry.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/api/test_server_startup_scripts.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/api/test_task_model_contract.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/api/test_vqa.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/config/test_architecture.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/config/test_editable.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/config/test_optional_hf.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/config/test_pretrained_merge.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/config/test_split_overrides.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/contracts/test_architecture.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/contracts/test_package.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/contracts/test_release_versions.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/contracts/test_runner_contract.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/core/test_classification_trainer.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/core/test_localization_hf_backend.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/core/test_training_components.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/core/test_video_processing.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/data/test_classification_paths.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/data/test_h5_tracking.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/data/test_localization_dali.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/data/test_osl_formats.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/models/test_classification_metrics.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/models/test_header_rule_spotter.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/models/test_header_skeleton_spotter.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/models/test_localization_metrics.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/models/test_temporal_shift.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/models/test_vqa_metrics.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/models/test_vqa_qwen_xvars.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/models/test_vqa_training_lora.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/models/test_vqa_xvars_videochatgpt.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/tools/test_conversion.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/tools/test_extract_xvars_features.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/tools/test_hf_transfer.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/tools/test_report_summary.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tests/unit/tools/test_setup.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/build_sn_vqa_2026_vqa.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/build_sngar_spotting.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/build_soccernet_gar.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/build_soccernet_gar_vqa.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/build_xvars_indexes.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/extract_xvars_clip_features.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/sngar_dataset_card.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/sngar_events.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/convert/verify_sngar_spotting.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/download/download_hf_repo.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/download/download_osl_hf.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/download/push_sngar_spotting.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/download/upload_osl_hf.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/training/classification.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/training/localization.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/tools/training/vqa.py +0 -0
- {opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/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.1.
|
|
3
|
+
Version: 0.3.1.dev19
|
|
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
|
|
@@ -28,6 +28,7 @@ Requires-Dist: pandas
|
|
|
28
28
|
Requires-Dist: pyarrow
|
|
29
29
|
Requires-Dist: h5py==3.16.0
|
|
30
30
|
Requires-Dist: huggingface_hub
|
|
31
|
+
Requires-Dist: datasets<6,>=5.0.0
|
|
31
32
|
Requires-Dist: easydict
|
|
32
33
|
Provides-Extra: test
|
|
33
34
|
Requires-Dist: pytest; extra == "test"
|
|
@@ -437,6 +438,44 @@ VQA setup workflow.
|
|
|
437
438
|
|
|
438
439
|
OpenSportsLib provides APIs and scripts for downloading and uploading OSL datasets with Hugging Face.
|
|
439
440
|
|
|
441
|
+
For SN-GAR tracking classification, `sngar_tracking_hf.yaml` loads split metadata
|
|
442
|
+
through `datasets` and caches every referenced TAR shard before using each split.
|
|
443
|
+
It preserves the weighted replacement sampler without extracting individual clips. Install
|
|
444
|
+
with `python -m pip install -e .`, authenticate with `hf auth login`,
|
|
445
|
+
and see [SN-GAR-README.md](SN-GAR-README.md) for the training command and cache layout.
|
|
446
|
+
|
|
447
|
+
For SN-GAR video action spotting, `sngar_spotting_video_hf.yaml` uses
|
|
448
|
+
`DATA.inputs.video.source.format: hf_json` to stage OSL JSON manifests and MP4s
|
|
449
|
+
from the `multimodal` branch of `OpenSportsLab/SNGAR-Action-Spotting`. It reads
|
|
450
|
+
the JSON manifests and downloads only their selected video inputs, leaving the
|
|
451
|
+
tracking Parquet files alone. Run
|
|
452
|
+
`LocalizationModel(config="sngar_spotting_video_hf.yaml").train(use_wandb=False)`
|
|
453
|
+
after `hf auth login` and dataset access approval. The first train/validation
|
|
454
|
+
stage downloads all videos in those splits; subsequent runs reuse the cache.
|
|
455
|
+
If a cached video is missing, staging downloads it again before training starts.
|
|
456
|
+
The example keeps the 300-frame window but uses one clip per step, limited
|
|
457
|
+
DataLoader prefetch, and four gradient accumulation steps to control memory.
|
|
458
|
+
For the OpenCV loader, accumulation combines successive batches, so batch size
|
|
459
|
+
one with four accumulation steps is valid.
|
|
460
|
+
The 29.97-fps videos are sampled every sixth source frame for approximately
|
|
461
|
+
5 fps and a 60-second window. Restart training after a sampling change so
|
|
462
|
+
decoded clips and event labels use the same frame rate.
|
|
463
|
+
|
|
464
|
+
For tracking action spotting, use
|
|
465
|
+
`LocalizationModel(config="sngar_spotting_tracking_hf.yaml").train(use_wandb=False)`.
|
|
466
|
+
This config selects `tracking_parquet` from the same `multimodal` JSON manifests,
|
|
467
|
+
downloads only the whole-game tracking tables in the requested splits,
|
|
468
|
+
and passes them to the tracking graph dataset. The staged manifest keeps the
|
|
469
|
+
`tracking_parquet` input type. Tracking files are cached under
|
|
470
|
+
`/home/giancos/OSLdata/sngar/hf_json_tracking_cache`; video MP4s are skipped.
|
|
471
|
+
|
|
472
|
+
To run the five example baselines sequentially with their full epoch settings,
|
|
473
|
+
see [tools/train/weekend_train.sh](tools/train/weekend_train.sh). Run it with
|
|
474
|
+
`bash tools/train/weekend_train.sh` after `hf auth login`. Each algorithm and
|
|
475
|
+
dataset has its own section in the script. Video spotting alone was measured at
|
|
476
|
+
about 10 days for 100 epochs on one user's machine, so the complete sequence
|
|
477
|
+
will extend beyond a weekend on similar hardware.
|
|
478
|
+
|
|
440
479
|
### Python API
|
|
441
480
|
|
|
442
481
|
```python
|
|
@@ -399,6 +399,44 @@ VQA setup workflow.
|
|
|
399
399
|
|
|
400
400
|
OpenSportsLib provides APIs and scripts for downloading and uploading OSL datasets with Hugging Face.
|
|
401
401
|
|
|
402
|
+
For SN-GAR tracking classification, `sngar_tracking_hf.yaml` loads split metadata
|
|
403
|
+
through `datasets` and caches every referenced TAR shard before using each split.
|
|
404
|
+
It preserves the weighted replacement sampler without extracting individual clips. Install
|
|
405
|
+
with `python -m pip install -e .`, authenticate with `hf auth login`,
|
|
406
|
+
and see [SN-GAR-README.md](SN-GAR-README.md) for the training command and cache layout.
|
|
407
|
+
|
|
408
|
+
For SN-GAR video action spotting, `sngar_spotting_video_hf.yaml` uses
|
|
409
|
+
`DATA.inputs.video.source.format: hf_json` to stage OSL JSON manifests and MP4s
|
|
410
|
+
from the `multimodal` branch of `OpenSportsLab/SNGAR-Action-Spotting`. It reads
|
|
411
|
+
the JSON manifests and downloads only their selected video inputs, leaving the
|
|
412
|
+
tracking Parquet files alone. Run
|
|
413
|
+
`LocalizationModel(config="sngar_spotting_video_hf.yaml").train(use_wandb=False)`
|
|
414
|
+
after `hf auth login` and dataset access approval. The first train/validation
|
|
415
|
+
stage downloads all videos in those splits; subsequent runs reuse the cache.
|
|
416
|
+
If a cached video is missing, staging downloads it again before training starts.
|
|
417
|
+
The example keeps the 300-frame window but uses one clip per step, limited
|
|
418
|
+
DataLoader prefetch, and four gradient accumulation steps to control memory.
|
|
419
|
+
For the OpenCV loader, accumulation combines successive batches, so batch size
|
|
420
|
+
one with four accumulation steps is valid.
|
|
421
|
+
The 29.97-fps videos are sampled every sixth source frame for approximately
|
|
422
|
+
5 fps and a 60-second window. Restart training after a sampling change so
|
|
423
|
+
decoded clips and event labels use the same frame rate.
|
|
424
|
+
|
|
425
|
+
For tracking action spotting, use
|
|
426
|
+
`LocalizationModel(config="sngar_spotting_tracking_hf.yaml").train(use_wandb=False)`.
|
|
427
|
+
This config selects `tracking_parquet` from the same `multimodal` JSON manifests,
|
|
428
|
+
downloads only the whole-game tracking tables in the requested splits,
|
|
429
|
+
and passes them to the tracking graph dataset. The staged manifest keeps the
|
|
430
|
+
`tracking_parquet` input type. Tracking files are cached under
|
|
431
|
+
`/home/giancos/OSLdata/sngar/hf_json_tracking_cache`; video MP4s are skipped.
|
|
432
|
+
|
|
433
|
+
To run the five example baselines sequentially with their full epoch settings,
|
|
434
|
+
see [tools/train/weekend_train.sh](tools/train/weekend_train.sh). Run it with
|
|
435
|
+
`bash tools/train/weekend_train.sh` after `hf auth login`. Each algorithm and
|
|
436
|
+
dataset has its own section in the script. Video spotting alone was measured at
|
|
437
|
+
about 10 days for 100 epochs on one user's machine, so the complete sequence
|
|
438
|
+
will extend beyond a weekend on similar hardware.
|
|
439
|
+
|
|
402
440
|
### Python API
|
|
403
441
|
|
|
404
442
|
```python
|
{opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/apis/classification.py
RENAMED
|
@@ -33,6 +33,19 @@ class ClassificationModel(BaseTaskModel):
|
|
|
33
33
|
"""Top-level task wrapper for classification."""
|
|
34
34
|
|
|
35
35
|
def _resolve_split_path(self, split: str, override: str | None = None) -> str:
|
|
36
|
+
if not self.is_remote:
|
|
37
|
+
from opensportslib.datasets.hf_tracking import (
|
|
38
|
+
hf_tracking_source,
|
|
39
|
+
prepare_hf_tracking_split,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if hf_tracking_source(self.config):
|
|
43
|
+
if override is not None:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
"hf_webdataset uses its configured Hub splits; "
|
|
46
|
+
"train_set, valid_set, and test_set overrides are unsupported."
|
|
47
|
+
)
|
|
48
|
+
return str(prepare_hf_tracking_split(self.config, split).annotations_path)
|
|
36
49
|
if override is not None:
|
|
37
50
|
return expand(override)
|
|
38
51
|
|
|
@@ -16,6 +16,7 @@ from opensportslib.core.config.accessors import (
|
|
|
16
16
|
get_split_annotation_path,
|
|
17
17
|
get_split_cfg,
|
|
18
18
|
set_split_annotation_path,
|
|
19
|
+
set_split_source_path,
|
|
19
20
|
set_loader_backend,
|
|
20
21
|
get_model_family,
|
|
21
22
|
)
|
|
@@ -49,6 +50,15 @@ class LocalizationModel(BaseTaskModel):
|
|
|
49
50
|
if override is not None:
|
|
50
51
|
return expand(override)
|
|
51
52
|
|
|
53
|
+
from opensportslib.datasets.hf_json import hf_json_source, prepare_hf_json_split
|
|
54
|
+
|
|
55
|
+
if hf_json_source(self.config):
|
|
56
|
+
prepared = prepare_hf_json_split(self.config, split)
|
|
57
|
+
set_split_source_path(self.config, split, str(prepared.source_path))
|
|
58
|
+
if split == "valid":
|
|
59
|
+
set_split_source_path(self.config, "valid_data_frames", str(prepared.source_path))
|
|
60
|
+
return str(prepared.annotation_path)
|
|
61
|
+
|
|
52
62
|
path = get_split_annotation_path(self.config, split)
|
|
53
63
|
if path:
|
|
54
64
|
return expand(path)
|
{opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/config/accessors.py
RENAMED
|
@@ -182,6 +182,20 @@ 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 set_split_source_path(cfg: Any, split: str, path: str) -> None:
|
|
186
|
+
data = getattr(cfg, "DATA", None)
|
|
187
|
+
if data is None:
|
|
188
|
+
data = SimpleNamespace()
|
|
189
|
+
setattr(cfg, "DATA", data)
|
|
190
|
+
common = _ensure_child(data, "common")
|
|
191
|
+
splits = _ensure_child(common, "splits")
|
|
192
|
+
split_cfg = _ensure_child(splits, split)
|
|
193
|
+
if isinstance(split_cfg, dict):
|
|
194
|
+
split_cfg["source_path"] = path
|
|
195
|
+
else:
|
|
196
|
+
setattr(split_cfg, "source_path", path)
|
|
197
|
+
|
|
198
|
+
|
|
185
199
|
def classes_to_ordered_list(classes: Any) -> list[str]:
|
|
186
200
|
"""Normalise a classes spec to a list ordered by class index.
|
|
187
201
|
|
{opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/load_annotations.py
RENAMED
|
@@ -860,9 +860,16 @@ def check_config(cfg, split="train"):
|
|
|
860
860
|
]
|
|
861
861
|
head_type = get_component_name_by_kind(cfg, "head")
|
|
862
862
|
assert head_type in ["", "gru", "deeper_gru", "mstcn", "asformer"]
|
|
863
|
-
# assert cfg.dataset.batch_size % cfg.training.acc_grad_iter == 0
|
|
864
863
|
train_bs = cfg.DATA.common.splits.train.dataloader.batch_size
|
|
865
|
-
|
|
864
|
+
acc_grad_iter = cfg.TRAIN.execution.acc_grad_iter
|
|
865
|
+
if acc_grad_iter < 1:
|
|
866
|
+
raise ValueError("TRAIN.execution.acc_grad_iter must be at least 1")
|
|
867
|
+
# DALI divides its configured batch into accumulation microbatches.
|
|
868
|
+
# OpenCV accumulates gradients across independent DataLoader batches.
|
|
869
|
+
if get_loader_backend(cfg) == "dali" and train_bs % acc_grad_iter != 0:
|
|
870
|
+
raise ValueError(
|
|
871
|
+
"DALI train batch_size must be divisible by TRAIN.execution.acc_grad_iter"
|
|
872
|
+
)
|
|
866
873
|
assert cfg.TRAIN.execution.criterion_valid in ["map", "loss"]
|
|
867
874
|
assert get_train_epochs(cfg) == cfg.TRAIN.scheduler.num_epochs
|
|
868
875
|
assert cfg.TRAIN.execution.acc_grad_iter == cfg.TRAIN.scheduler.acc_grad_iter
|
{opensportslib-0.3.1.dev18 → opensportslib-0.3.1.dev19}/opensportslib/core/utils/video_processing.py
RENAMED
|
@@ -99,6 +99,13 @@ def get_stride(src_fps, sample_fps):
|
|
|
99
99
|
if sample_fps <= 0:
|
|
100
100
|
stride = 1
|
|
101
101
|
else:
|
|
102
|
+
# MP4s commonly report 30000/1001 rather than the nominal 30 fps.
|
|
103
|
+
# Truncating 29.97 / 5 gives 5, which samples at almost 6 fps.
|
|
104
|
+
nominal_fps = round(src_fps)
|
|
105
|
+
if nominal_fps > 0 and math.isclose(
|
|
106
|
+
src_fps, nominal_fps, rel_tol=0.0011
|
|
107
|
+
):
|
|
108
|
+
src_fps = nominal_fps
|
|
102
109
|
stride = int(src_fps / sample_fps)
|
|
103
110
|
return stride
|
|
104
111
|
|
|
@@ -65,6 +65,10 @@ def build(config, annotations_path, processor=None, split="train"):
|
|
|
65
65
|
modality = get_data_modality(config).lower()
|
|
66
66
|
|
|
67
67
|
if modality in ("tracking", "tracking_parquet"):
|
|
68
|
+
from opensportslib.datasets.hf_tracking import hf_tracking_source
|
|
69
|
+
|
|
70
|
+
if hf_tracking_source(config):
|
|
71
|
+
return HFTrackingDataset(config, split=split)
|
|
68
72
|
return TrackingDataset(config, annotations_path, split)
|
|
69
73
|
elif modality in H5_TRACKING_MODALITIES:
|
|
70
74
|
return H5TrackingDataset(config, annotations_path, split)
|
|
@@ -749,6 +753,22 @@ class TrackingDataset(ClassificationDataset):
|
|
|
749
753
|
return out
|
|
750
754
|
|
|
751
755
|
|
|
756
|
+
class HFTrackingDataset(TrackingDataset):
|
|
757
|
+
"""Tracking graphs backed by indexed Hugging Face WebDataset TAR shards."""
|
|
758
|
+
|
|
759
|
+
def __init__(self, config, split="train"):
|
|
760
|
+
from opensportslib.datasets.hf_tracking import HFTarTrackingReader, prepare_hf_tracking_split
|
|
761
|
+
|
|
762
|
+
if get_data_params(config).get("preload_data", False):
|
|
763
|
+
raise ValueError("hf_webdataset requires DATA.inputs.tracking.params.preload_data=false")
|
|
764
|
+
prepared = prepare_hf_tracking_split(config, split)
|
|
765
|
+
self._hf_reader = HFTarTrackingReader(prepared)
|
|
766
|
+
super().__init__(config, str(prepared.annotations_path), split)
|
|
767
|
+
|
|
768
|
+
def _load_tracking_clip(self, path):
|
|
769
|
+
return self._hf_reader.read_parquet(path)
|
|
770
|
+
|
|
771
|
+
|
|
752
772
|
class H5TrackingDataset(ClassificationDataset):
|
|
753
773
|
"""Graph-based classification dataset for UTC-indexed player/ball H5 files."""
|
|
754
774
|
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Stage OSL JSON manifests and their selected media from a Hugging Face dataset."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import fcntl
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path, PurePosixPath
|
|
12
|
+
|
|
13
|
+
from opensportslib.core.config.accessors import get_input_cfg
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class PreparedHFJsonSplit:
|
|
18
|
+
annotation_path: Path
|
|
19
|
+
source_path: Path
|
|
20
|
+
revision: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def hf_json_source(config) -> dict:
|
|
24
|
+
source = get_input_cfg(config).get("source") or {}
|
|
25
|
+
return source if str(source.get("format", "")).lower() == "hf_json" else {}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _safe_path(value: str) -> str:
|
|
29
|
+
path = PurePosixPath(value)
|
|
30
|
+
if not value or path.is_absolute() or ".." in path.parts or "\\" in value:
|
|
31
|
+
raise ValueError(f"Unsafe path in Hugging Face JSON manifest: {value!r}")
|
|
32
|
+
return path.as_posix()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _set_resolved_revision(config, revision: str) -> None:
|
|
36
|
+
inputs = config.DATA.inputs
|
|
37
|
+
values = inputs.values() if isinstance(inputs, dict) else vars(inputs).values()
|
|
38
|
+
for input_cfg in values:
|
|
39
|
+
source = input_cfg.get("source") if isinstance(input_cfg, dict) else input_cfg.source
|
|
40
|
+
if str(source.get("format") if isinstance(source, dict) else source.format).lower() == "hf_json":
|
|
41
|
+
if isinstance(source, dict):
|
|
42
|
+
source["resolved_revision"] = revision
|
|
43
|
+
else:
|
|
44
|
+
source.resolved_revision = revision
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def prepare_hf_json_split(config, split: str) -> PreparedHFJsonSplit:
|
|
49
|
+
"""Download one split's selected media, then publish its local manifest.
|
|
50
|
+
|
|
51
|
+
The localization video and tracking loaders inspect each file during
|
|
52
|
+
construction. Stage only the configured input type while keeping its
|
|
53
|
+
manifest type and other metadata intact. Hub blobs are linked into a
|
|
54
|
+
small split directory.
|
|
55
|
+
"""
|
|
56
|
+
source = hf_json_source(config)
|
|
57
|
+
if not source:
|
|
58
|
+
raise ValueError("Source format must be 'hf_json'.")
|
|
59
|
+
if split not in {"train", "valid", "test"}:
|
|
60
|
+
raise ValueError(f"Unsupported Hugging Face JSON split: {split!r}")
|
|
61
|
+
|
|
62
|
+
repo_id = str(source.get("repo_id") or "").strip()
|
|
63
|
+
branch = str(source.get("revision") or "main").strip()
|
|
64
|
+
cache_dir = Path(str(source.get("cache_dir") or "~/.cache/opensportslib/hf_json")).expanduser()
|
|
65
|
+
media_type = str(source.get("input_type") or "video_mp4")
|
|
66
|
+
if not repo_id or not branch:
|
|
67
|
+
raise ValueError("hf_json requires source.repo_id and source.revision.")
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
from huggingface_hub import HfApi, hf_hub_download
|
|
71
|
+
|
|
72
|
+
revision = str(source.get("resolved_revision") or "").strip()
|
|
73
|
+
if not revision:
|
|
74
|
+
revision = HfApi(token=True).repo_info(
|
|
75
|
+
repo_id=repo_id, repo_type="dataset", revision=branch
|
|
76
|
+
).sha
|
|
77
|
+
if not revision:
|
|
78
|
+
raise ValueError("The Hub did not return a commit SHA.")
|
|
79
|
+
except Exception as exc:
|
|
80
|
+
raise RuntimeError(
|
|
81
|
+
f"Cannot access Hugging Face dataset {repo_id!r} at {branch!r}. "
|
|
82
|
+
"Request access and run `hf auth login`."
|
|
83
|
+
) from exc
|
|
84
|
+
|
|
85
|
+
_set_resolved_revision(config, revision)
|
|
86
|
+
safe_repo = re.sub(r"[^A-Za-z0-9_.-]+", "--", repo_id)
|
|
87
|
+
root = cache_dir / safe_repo / revision
|
|
88
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
89
|
+
manifest_name = _safe_path(str(source.get("annotation_pattern") or "annotations_{split}.json").format(split=split))
|
|
90
|
+
annotation_path = root / f"selected_{media_type}_{split}.json"
|
|
91
|
+
prepared = PreparedHFJsonSplit(annotation_path, root, revision)
|
|
92
|
+
hub_cache = cache_dir / "hub"
|
|
93
|
+
|
|
94
|
+
with (root / f".{split}.lock").open("w") as lock:
|
|
95
|
+
fcntl.flock(lock, fcntl.LOCK_EX)
|
|
96
|
+
try:
|
|
97
|
+
cached_manifest = annotation_path.is_file()
|
|
98
|
+
if cached_manifest:
|
|
99
|
+
payload = json.loads(annotation_path.read_text(encoding="utf-8"))
|
|
100
|
+
# Older caches could contain an input relabeled to ``video``.
|
|
101
|
+
# Refresh those manifests so the selected source type is kept.
|
|
102
|
+
valid_cache = isinstance(payload, dict) and isinstance(payload.get("data"), list)
|
|
103
|
+
if valid_cache:
|
|
104
|
+
valid_cache = all(
|
|
105
|
+
isinstance(sample, dict)
|
|
106
|
+
and len([
|
|
107
|
+
item for item in sample.get("inputs", [])
|
|
108
|
+
if isinstance(item, dict) and item.get("type") == media_type
|
|
109
|
+
]) == 1
|
|
110
|
+
for sample in payload["data"]
|
|
111
|
+
)
|
|
112
|
+
if not valid_cache:
|
|
113
|
+
cached_manifest = False
|
|
114
|
+
if not cached_manifest:
|
|
115
|
+
remote_manifest = hf_hub_download(
|
|
116
|
+
repo_id=repo_id, filename=manifest_name, repo_type="dataset",
|
|
117
|
+
revision=revision, token=True, cache_dir=str(hub_cache),
|
|
118
|
+
)
|
|
119
|
+
payload = json.loads(Path(remote_manifest).read_text(encoding="utf-8"))
|
|
120
|
+
if not isinstance(payload, dict) or not isinstance(payload.get("data"), list):
|
|
121
|
+
raise ValueError(f"{manifest_name} is not an OSL JSON manifest")
|
|
122
|
+
if not payload["data"]:
|
|
123
|
+
raise ValueError(f"{manifest_name} contains no samples")
|
|
124
|
+
|
|
125
|
+
filenames = set()
|
|
126
|
+
for sample in payload["data"]:
|
|
127
|
+
selected = [item for item in sample.get("inputs", []) if item.get("type") == media_type]
|
|
128
|
+
if len(selected) != 1:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
f"Expected one {media_type!r} input for sample {sample.get('game_id', sample.get('id'))!r}"
|
|
131
|
+
)
|
|
132
|
+
item = dict(selected[0])
|
|
133
|
+
filename = _safe_path(str(item.get("path") or ""))
|
|
134
|
+
if not filename.startswith(f"{split}/"):
|
|
135
|
+
raise ValueError(f"Media path {filename!r} is outside split {split!r}")
|
|
136
|
+
filenames.add(filename)
|
|
137
|
+
sample["inputs"] = [item]
|
|
138
|
+
|
|
139
|
+
def stage_media(filename: str) -> None:
|
|
140
|
+
destination = root / filename
|
|
141
|
+
if destination.is_file():
|
|
142
|
+
return
|
|
143
|
+
downloaded = Path(hf_hub_download(
|
|
144
|
+
repo_id=repo_id, filename=filename, repo_type="dataset",
|
|
145
|
+
revision=revision, token=True, cache_dir=str(hub_cache),
|
|
146
|
+
))
|
|
147
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
148
|
+
temporary = destination.with_name(f".{destination.name}.{os.getpid()}.tmp")
|
|
149
|
+
try:
|
|
150
|
+
temporary.symlink_to(downloaded)
|
|
151
|
+
os.replace(temporary, destination)
|
|
152
|
+
finally:
|
|
153
|
+
temporary.unlink(missing_ok=True)
|
|
154
|
+
|
|
155
|
+
with ThreadPoolExecutor(max_workers=min(4, len(filenames))) as executor:
|
|
156
|
+
list(executor.map(stage_media, sorted(filenames)))
|
|
157
|
+
|
|
158
|
+
if not cached_manifest:
|
|
159
|
+
temporary = annotation_path.with_name(f".{annotation_path.name}.{os.getpid()}.tmp")
|
|
160
|
+
try:
|
|
161
|
+
temporary.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8")
|
|
162
|
+
os.replace(temporary, annotation_path)
|
|
163
|
+
finally:
|
|
164
|
+
temporary.unlink(missing_ok=True)
|
|
165
|
+
except Exception as exc:
|
|
166
|
+
raise RuntimeError(
|
|
167
|
+
f"Failed to stage {split!r} from {repo_id}@{revision}: {exc}"
|
|
168
|
+
) from exc
|
|
169
|
+
return prepared
|