opensportslib 0.3.0.dev19__tar.gz → 0.3.0.dev21__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.0.dev19/opensportslib.egg-info → opensportslib-0.3.0.dev21}/PKG-INFO +1 -1
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/setup/setup.py +0 -9
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/tools/hf_transfer.py +160 -22
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/tools/osl_json_to_parquet.py +74 -12
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/tools/parquet_to_osl_json.py +33 -17
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21/opensportslib.egg-info}/PKG-INFO +1 -1
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/pyproject.toml +1 -1
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_hf_transfer_tools.py +161 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/LICENSE +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/LICENSE-COMMERCIAL +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/MANIFEST.in +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/README.md +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/examples/quickstart/basic_classification.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/examples/quickstart/basic_localization.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/examples/quickstart/basic_vqa.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/adaptation/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/adaptation/spotta.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/apis/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/apis/base_task_model.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/apis/classification.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/apis/localization.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/apis/vqa.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/cli.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/classification/default.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/classification/sngar_frames.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/classification/sngar_tracking.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/classification/video.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/default.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/localization/calf_resnetpca512.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/localization/default.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/localization/e2e_spotta.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/localization/h5_header_distance.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/localization/h5_header_skeleton.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/localization/netvladpp_resnetpca512.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/localization/tracking_action_spotting.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/localization/video_dali.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/localization/video_ocv.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/vqa/default.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/vqa/qwen.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/vqa/qwen3_vl_native.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/vqa/qwen_lora.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/vqa/qwen_sngar_frames.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/configs/vqa/xvars.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/accessors.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/conflicts.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/loader.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/migrate.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/migrations/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/migrations/legacy_to_canonical.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/runtime_adapter.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/schema.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/schemas/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/schemas/schema_canonical.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/schemas/schema_legacy.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/config/validate.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/loss/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/loss/builder.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/loss/calf.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/loss/ce.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/loss/combine.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/loss/nll.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/optimizer/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/optimizer/builder.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/sampler/weighted_sampler.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/scheduler/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/scheduler/builder.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/trainer/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/trainer/classification_trainer.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/trainer/localization_trainer.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/trainer/vqa_trainer.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/checkpoint.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/config.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/config_normalize.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/data.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/ddp.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/default_args.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/hf_runtime.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/lightning.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/load_annotations.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/seed.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/video_processing.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/core/utils/wandb.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/datasets/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/datasets/builder.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/datasets/classification_dataset.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/datasets/localization_dataset.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/datasets/utils/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/datasets/utils/h5_tracking.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/datasets/utils/tracking.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/datasets/vqa_dataset.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/legacy_config/classification.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/legacy_config/localization-e2e-ocv.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/legacy_config/localization-json_calf_resnetpca512.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/legacy_config/localization-json_netvlad++_resnetpca512.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/legacy_config/localization.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/legacy_config/sngar-frames.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/legacy_config/sngar-tracking.yaml +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/metrics/classification_metric.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/metrics/localization_metric.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/metrics/vqa_metric.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/backbones/builder.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/contextaware.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/e2e.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/learnablepooling.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/qwen_vl_native.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/qwen_xvars.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/rule_based.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/tracking.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/vars.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/video.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/video_chatgpt_compat.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/video_mae.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/base/xvars_videochatgpt.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/builder.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/heads/builder.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/neck/builder.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/common.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/impl/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/impl/asformer.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/impl/calf.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/impl/gsm.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/impl/gtad.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/impl/tsm.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/litebase.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/modules.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/shift.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/utils.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/vqa_prediction_priors.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/vqa_prompting.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/models/utils/xvars_clip_index.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/tools/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/tools/_common.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib.egg-info/SOURCES.txt +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib.egg-info/dependency_links.txt +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib.egg-info/entry_points.txt +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib.egg-info/requires.txt +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib.egg-info/top_level.txt +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/scripts/run_h5_header_rule_inference.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/setup.cfg +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/conftest.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/release/__init__.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/release/_release_common.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/release/test_classification_release.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/release/test_localization_release.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/release/test_vqa_release.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_classification_dataset_paths.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_classification_trainer_dataloader.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_config_architecture.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_config_split_override_sync.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_config_utils_smoke.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_conversion_tools.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_extract_xvars_features.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_h5_header_rule_spotter.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_h5_header_skeleton_spotter.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_h5_tracking_dataset.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_localization_dali_filenames.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_localization_hf_backend_override.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_localization_intervals.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_package_smoke.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_pretrained_config_merge_policy.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_public_apis_smoke.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_setup_cli.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_spotta_e2e.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_subset_train_infer_integration.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_task_model_api_contract.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_vqa_api.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_vqa_metrics_semantic.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_vqa_qwen_xvars.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_vqa_training_lora.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tests/test_vqa_xvars_videochatgpt.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/build_sn_vqa_2026_vqa.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/build_sngar_spotting.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/build_soccernet_gar.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/build_soccernet_gar_vqa.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/build_xvars_indexes.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/extract_xvars_clip_features.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/sngar_dataset_card.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/sngar_events.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/convert/verify_sngar_spotting.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/download/download_hf_repo.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/download/download_osl_hf.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/download/push_sngar_spotting.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/download/upload_osl_hf.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/training/classification.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/training/localization.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/tools/training/vqa.py +0 -0
- {opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/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.
|
|
3
|
+
Version: 0.3.0.dev21
|
|
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
|
|
@@ -13,11 +13,6 @@ LEGACY_GPU_MAX_COMPUTE_CAPABILITY = (7, 4)
|
|
|
13
13
|
LEGACY_GPU_CUDA_WHEEL = "cu126"
|
|
14
14
|
LEGACY_GPU_CUDA_WHEEL_MAX_COMPUTE_CAPABILITY = (9, 0)
|
|
15
15
|
CUDA13_REQUIRED_MIN_COMPUTE_CAPABILITY = (10, 0)
|
|
16
|
-
LEGACY_GPU_TORCH_PACKAGES = (
|
|
17
|
-
"torch==2.10.0",
|
|
18
|
-
"torchvision==0.25.0",
|
|
19
|
-
"torchaudio==2.10.0",
|
|
20
|
-
)
|
|
21
16
|
|
|
22
17
|
XVARS_DEPENDENCY_PINS = {
|
|
23
18
|
"transformers": "4.38.2",
|
|
@@ -140,10 +135,6 @@ def select_cuda_wheel(cuda_version, compute_capabilities):
|
|
|
140
135
|
|
|
141
136
|
|
|
142
137
|
def select_torch_packages(compute_capabilities):
|
|
143
|
-
# if compute_capabilities and any(
|
|
144
|
-
# capability <= LEGACY_GPU_MAX_COMPUTE_CAPABILITY for capability in compute_capabilities
|
|
145
|
-
# ):
|
|
146
|
-
# return LEGACY_GPU_TORCH_PACKAGES
|
|
147
138
|
return ("torch", "torchvision", "torchaudio")
|
|
148
139
|
|
|
149
140
|
|
|
@@ -117,9 +117,13 @@ def extract_repo_paths_from_json(
|
|
|
117
117
|
if not isinstance(inp, dict):
|
|
118
118
|
continue
|
|
119
119
|
path = inp.get("path")
|
|
120
|
-
if
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
if path:
|
|
121
|
+
repo_paths.append(str(path).lstrip("/"))
|
|
122
|
+
# player_joints_h5 / player_centroids_h5 inputs may carry a
|
|
123
|
+
# sidecar ball_path; download it alongside the primary input.
|
|
124
|
+
ball_path = inp.get("ball_path")
|
|
125
|
+
if ball_path:
|
|
126
|
+
repo_paths.append(str(ball_path).lstrip("/"))
|
|
123
127
|
|
|
124
128
|
if not repo_paths:
|
|
125
129
|
raise ValueError("No file paths found in the provided JSON (no inputs with 'path').")
|
|
@@ -435,6 +439,131 @@ def _download_json_path_from_hf(
|
|
|
435
439
|
return result
|
|
436
440
|
|
|
437
441
|
|
|
442
|
+
_PREFERRED_SPLIT_ORDER = ["train", "valid", "val", "validation", "test", "challenge"]
|
|
443
|
+
_NON_SPLIT_JSON_FILES = {"dataset_infos.json", "dataset_dict.json"}
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def _sort_splits(splits: set[str]) -> list[str]:
|
|
447
|
+
def _sort_key(name: str) -> tuple[int, str]:
|
|
448
|
+
try:
|
|
449
|
+
rank = _PREFERRED_SPLIT_ORDER.index(name.lower())
|
|
450
|
+
except ValueError:
|
|
451
|
+
rank = len(_PREFERRED_SPLIT_ORDER)
|
|
452
|
+
return (rank, name.lower())
|
|
453
|
+
|
|
454
|
+
return sorted(splits, key=_sort_key)
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def list_dataset_branches_on_hf(
|
|
458
|
+
repo_id: str,
|
|
459
|
+
*,
|
|
460
|
+
token: str | None = None,
|
|
461
|
+
) -> list[str]:
|
|
462
|
+
cleaned_repo_id = str(repo_id or "").strip()
|
|
463
|
+
if not cleaned_repo_id:
|
|
464
|
+
raise ValueError("repo_id is required.")
|
|
465
|
+
|
|
466
|
+
HfApi, _, _ = _import_hf_hub()
|
|
467
|
+
api = HfApi(token=token or None)
|
|
468
|
+
refs = api.list_repo_refs(cleaned_repo_id, repo_type="dataset")
|
|
469
|
+
branch_names = [str(branch.name) for branch in getattr(refs, "branches", [])]
|
|
470
|
+
|
|
471
|
+
unique_names = sorted(set(branch_names))
|
|
472
|
+
if "main" in unique_names:
|
|
473
|
+
unique_names.remove("main")
|
|
474
|
+
return ["main"] + unique_names
|
|
475
|
+
return unique_names
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def list_dataset_splits_on_hf(
|
|
479
|
+
repo_id: str,
|
|
480
|
+
revision: str,
|
|
481
|
+
*,
|
|
482
|
+
token: str | None = None,
|
|
483
|
+
) -> dict[str, Any]:
|
|
484
|
+
cleaned_repo_id = str(repo_id or "").strip()
|
|
485
|
+
cleaned_revision = str(revision or "").strip() or "main"
|
|
486
|
+
if not cleaned_repo_id:
|
|
487
|
+
raise ValueError("repo_id is required.")
|
|
488
|
+
|
|
489
|
+
HfApi, _, _ = _import_hf_hub()
|
|
490
|
+
api = HfApi(token=token or None)
|
|
491
|
+
repo_files = api.list_repo_files(
|
|
492
|
+
cleaned_repo_id,
|
|
493
|
+
revision=cleaned_revision,
|
|
494
|
+
repo_type="dataset",
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
parquet_splits: set[str] = set()
|
|
498
|
+
json_splits: set[str] = set()
|
|
499
|
+
for path in repo_files:
|
|
500
|
+
normalized = _normalize_repo_path(path)
|
|
501
|
+
if "/" in normalized:
|
|
502
|
+
folder, filename = normalized.split("/", 1)
|
|
503
|
+
# Only the canonical Parquet+WebDataset export layout counts as a
|
|
504
|
+
# parquet split (produced by convert_json_to_parquet / expected by
|
|
505
|
+
# convert_parquet_to_json): `{split}/metadata.parquet` plus TAR
|
|
506
|
+
# shards under `{split}/shards/`. A JSON-format dataset can also
|
|
507
|
+
# reference arbitrary `.parquet` media files (e.g. tensor-encoded
|
|
508
|
+
# videos) under a folder that happens to share the split's name,
|
|
509
|
+
# so a loose "any .parquet/.tar anywhere under this folder" check
|
|
510
|
+
# would misclassify those as Parquet+WebDataset splits.
|
|
511
|
+
if folder and (
|
|
512
|
+
filename == "metadata.parquet"
|
|
513
|
+
or (filename.startswith("shards/") and filename.lower().endswith(".tar"))
|
|
514
|
+
):
|
|
515
|
+
parquet_splits.add(folder)
|
|
516
|
+
elif normalized.lower().endswith(".json") and normalized not in _NON_SPLIT_JSON_FILES:
|
|
517
|
+
json_splits.add(normalized[: -len(".json")])
|
|
518
|
+
|
|
519
|
+
if parquet_splits:
|
|
520
|
+
return {"format": "parquet", "splits": _sort_splits(parquet_splits)}
|
|
521
|
+
if json_splits:
|
|
522
|
+
return {"format": "json", "splits": _sort_splits(json_splits)}
|
|
523
|
+
return {"format": None, "splits": []}
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def download_dataset_splits_from_hf(
|
|
527
|
+
repo_id: str,
|
|
528
|
+
revision: str,
|
|
529
|
+
splits: list[str],
|
|
530
|
+
output_dir: str,
|
|
531
|
+
*,
|
|
532
|
+
download_format: str = "parquet",
|
|
533
|
+
dry_run: bool = False,
|
|
534
|
+
token: str | None = None,
|
|
535
|
+
progress_cb: ProgressCallback | None = None,
|
|
536
|
+
is_cancelled: CancelCheck | None = None,
|
|
537
|
+
) -> list[dict[str, Any]]:
|
|
538
|
+
cleaned_splits = [str(split or "").strip() for split in (splits or [])]
|
|
539
|
+
cleaned_splits = [split for split in cleaned_splits if split]
|
|
540
|
+
if not cleaned_splits:
|
|
541
|
+
raise ValueError("At least one split is required.")
|
|
542
|
+
|
|
543
|
+
total = len(cleaned_splits)
|
|
544
|
+
results: list[dict[str, Any]] = []
|
|
545
|
+
for idx, split in enumerate(cleaned_splits, start=1):
|
|
546
|
+
_ensure_not_cancelled(is_cancelled)
|
|
547
|
+
|
|
548
|
+
def _scoped_progress(message: str, _idx: int = idx, _split: str = split) -> None:
|
|
549
|
+
_emit_progress(progress_cb, f"[{_idx}/{total}] {_split}: {message}")
|
|
550
|
+
|
|
551
|
+
result = download_dataset_split_from_hf(
|
|
552
|
+
repo_id,
|
|
553
|
+
revision,
|
|
554
|
+
split,
|
|
555
|
+
output_dir,
|
|
556
|
+
download_format=download_format,
|
|
557
|
+
dry_run=dry_run,
|
|
558
|
+
token=token,
|
|
559
|
+
progress_cb=_scoped_progress,
|
|
560
|
+
is_cancelled=is_cancelled,
|
|
561
|
+
)
|
|
562
|
+
results.append(result)
|
|
563
|
+
|
|
564
|
+
return results
|
|
565
|
+
|
|
566
|
+
|
|
438
567
|
def download_dataset_split_from_hf(
|
|
439
568
|
repo_id: str,
|
|
440
569
|
revision: str,
|
|
@@ -512,28 +641,37 @@ def extract_local_input_upload_entries_from_json(dataset_json_path: str) -> list
|
|
|
512
641
|
for inp in inputs:
|
|
513
642
|
if not isinstance(inp, dict):
|
|
514
643
|
continue
|
|
515
|
-
raw_path = str(inp.get("path") or "").strip()
|
|
516
|
-
if not raw_path:
|
|
517
|
-
continue
|
|
518
644
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
if
|
|
522
|
-
|
|
523
|
-
|
|
645
|
+
raw_paths = []
|
|
646
|
+
path = str(inp.get("path") or "").strip()
|
|
647
|
+
if path:
|
|
648
|
+
raw_paths.append(path)
|
|
649
|
+
# player_joints_h5 / player_centroids_h5 inputs may carry a sidecar
|
|
650
|
+
# ball_path pointing at a separate ball-tracking h5 file; include it
|
|
651
|
+
# alongside the primary input when present.
|
|
652
|
+
ball_path = str(inp.get("ball_path") or "").strip()
|
|
653
|
+
if ball_path:
|
|
654
|
+
raw_paths.append(ball_path)
|
|
655
|
+
|
|
656
|
+
for raw_path in raw_paths:
|
|
657
|
+
local_path = raw_path if os.path.isabs(raw_path) else os.path.join(base_dir, raw_path)
|
|
658
|
+
local_path = os.path.abspath(local_path)
|
|
659
|
+
if not os.path.isfile(local_path):
|
|
660
|
+
raise FileNotFoundError(
|
|
661
|
+
f"Input file from dataset JSON not found on disk: {raw_path} (resolved: {local_path})"
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
path_in_repo = _normalize_repo_path(raw_path)
|
|
665
|
+
if not path_in_repo:
|
|
666
|
+
raise ValueError(f"Invalid input path in dataset JSON: {raw_path}")
|
|
667
|
+
|
|
668
|
+
entries.append(
|
|
669
|
+
{
|
|
670
|
+
"local_path": local_path,
|
|
671
|
+
"path_in_repo": path_in_repo,
|
|
672
|
+
}
|
|
524
673
|
)
|
|
525
674
|
|
|
526
|
-
path_in_repo = _normalize_repo_path(raw_path)
|
|
527
|
-
if not path_in_repo:
|
|
528
|
-
raise ValueError(f"Invalid input path in dataset JSON: {raw_path}")
|
|
529
|
-
|
|
530
|
-
entries.append(
|
|
531
|
-
{
|
|
532
|
-
"local_path": local_path,
|
|
533
|
-
"path_in_repo": path_in_repo,
|
|
534
|
-
}
|
|
535
|
-
)
|
|
536
|
-
|
|
537
675
|
if not entries:
|
|
538
676
|
raise ValueError("No valid data[].inputs[].path entries found in the provided dataset JSON.")
|
|
539
677
|
return entries
|
{opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/tools/osl_json_to_parquet.py
RENAMED
|
@@ -149,9 +149,14 @@ def _estimate_sample_tar_size(
|
|
|
149
149
|
for input_item in _extract_inputs_with_path(sample):
|
|
150
150
|
rel_path = str(input_item["path"])
|
|
151
151
|
resolved = _resolve_media_path(media_root, rel_path, missing_policy=missing_policy)
|
|
152
|
-
if resolved is None:
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
if resolved is not None:
|
|
153
|
+
total += _tar_member_size(resolved.stat().st_size)
|
|
154
|
+
|
|
155
|
+
ball_rel_path = str(input_item.get("ball_path") or "").strip()
|
|
156
|
+
if ball_rel_path:
|
|
157
|
+
ball_resolved = _resolve_media_path(media_root, ball_rel_path, missing_policy=missing_policy)
|
|
158
|
+
if ball_resolved is not None:
|
|
159
|
+
total += _tar_member_size(ball_resolved.stat().st_size)
|
|
155
160
|
return total
|
|
156
161
|
|
|
157
162
|
|
|
@@ -312,6 +317,7 @@ def convert_json_to_parquet(
|
|
|
312
317
|
for input_idx, input_item in enumerate(_extract_inputs_with_path(sample)):
|
|
313
318
|
rel_path = str(input_item["path"])
|
|
314
319
|
resolved = _resolve_media_path(media_root, rel_path, missing_policy=missing_policy)
|
|
320
|
+
input_type = str(input_item.get("type", "")).strip()
|
|
315
321
|
|
|
316
322
|
if resolved is None:
|
|
317
323
|
total_missing_input_files += 1
|
|
@@ -319,25 +325,65 @@ def convert_json_to_parquet(
|
|
|
319
325
|
"sample_id": sample_id,
|
|
320
326
|
"shard_name": shard_name,
|
|
321
327
|
"input_index": input_idx,
|
|
322
|
-
"
|
|
328
|
+
"file_role": "primary",
|
|
329
|
+
"input_type": input_type,
|
|
323
330
|
"relative_path": rel_path,
|
|
324
331
|
"resolved_path": None,
|
|
325
332
|
"status": "missing",
|
|
326
333
|
})
|
|
334
|
+
else:
|
|
335
|
+
ext = resolved.suffix.lstrip(".").lower() or "bin"
|
|
336
|
+
arcname = f"{key}.{input_idx}.{ext}"
|
|
337
|
+
_add_file_to_tar(tar, resolved, arcname)
|
|
338
|
+
shard_manifest.append({
|
|
339
|
+
"sample_id": sample_id,
|
|
340
|
+
"shard_name": shard_name,
|
|
341
|
+
"input_index": input_idx,
|
|
342
|
+
"file_role": "primary",
|
|
343
|
+
"input_type": input_type,
|
|
344
|
+
"relative_path": rel_path,
|
|
345
|
+
"resolved_path": str(resolved if not keep_relative_paths_in_parquet else rel_path),
|
|
346
|
+
"status": "ok",
|
|
347
|
+
"wds_member": arcname,
|
|
348
|
+
})
|
|
349
|
+
total_input_files_added += 1
|
|
350
|
+
|
|
351
|
+
# player_joints_h5 / player_centroids_h5 inputs may carry a
|
|
352
|
+
# sidecar ball_path pointing at a separate ball-tracking h5
|
|
353
|
+
# file; bundle it into the same shard, tagged with a distinct
|
|
354
|
+
# arcname (".ball.") so it doesn't collide with the primary
|
|
355
|
+
# input's tar member.
|
|
356
|
+
ball_rel_path = str(input_item.get("ball_path") or "").strip()
|
|
357
|
+
if not ball_rel_path:
|
|
358
|
+
continue
|
|
359
|
+
ball_resolved = _resolve_media_path(media_root, ball_rel_path, missing_policy=missing_policy)
|
|
360
|
+
if ball_resolved is None:
|
|
361
|
+
total_missing_input_files += 1
|
|
362
|
+
shard_manifest.append({
|
|
363
|
+
"sample_id": sample_id,
|
|
364
|
+
"shard_name": shard_name,
|
|
365
|
+
"input_index": input_idx,
|
|
366
|
+
"file_role": "ball",
|
|
367
|
+
"input_type": input_type,
|
|
368
|
+
"relative_path": ball_rel_path,
|
|
369
|
+
"resolved_path": None,
|
|
370
|
+
"status": "missing",
|
|
371
|
+
})
|
|
327
372
|
continue
|
|
328
373
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
_add_file_to_tar(tar,
|
|
374
|
+
ball_ext = ball_resolved.suffix.lstrip(".").lower() or "bin"
|
|
375
|
+
ball_arcname = f"{key}.{input_idx}.ball.{ball_ext}"
|
|
376
|
+
_add_file_to_tar(tar, ball_resolved, ball_arcname)
|
|
332
377
|
shard_manifest.append({
|
|
333
378
|
"sample_id": sample_id,
|
|
334
379
|
"shard_name": shard_name,
|
|
335
380
|
"input_index": input_idx,
|
|
336
|
-
"
|
|
337
|
-
"
|
|
338
|
-
"
|
|
381
|
+
"file_role": "ball",
|
|
382
|
+
"input_type": input_type,
|
|
383
|
+
"relative_path": ball_rel_path,
|
|
384
|
+
"resolved_path": str(ball_resolved if not keep_relative_paths_in_parquet else ball_rel_path),
|
|
339
385
|
"status": "ok",
|
|
340
|
-
"wds_member":
|
|
386
|
+
"wds_member": ball_arcname,
|
|
341
387
|
})
|
|
342
388
|
total_input_files_added += 1
|
|
343
389
|
|
|
@@ -349,14 +395,25 @@ def convert_json_to_parquet(
|
|
|
349
395
|
ok_manifest = manifest_df[manifest_df["status"] == "ok"].copy()
|
|
350
396
|
if not ok_manifest.empty:
|
|
351
397
|
ok_manifest["input_index"] = ok_manifest["input_index"].astype(int)
|
|
398
|
+
primary_manifest = ok_manifest[ok_manifest["file_role"] == "primary"]
|
|
399
|
+
ball_manifest = ok_manifest[ok_manifest["file_role"] == "ball"]
|
|
400
|
+
|
|
352
401
|
by_sample_input_paths: Dict[str, Dict[int, str]] = {}
|
|
353
|
-
for sample_id, sample_manifest in
|
|
402
|
+
for sample_id, sample_manifest in primary_manifest.groupby("sample_id", sort=False):
|
|
354
403
|
sample_manifest = sample_manifest.sort_values("input_index")
|
|
355
404
|
by_sample_input_paths[str(sample_id)] = {
|
|
356
405
|
int(rec["input_index"]): str(rec["resolved_path"])
|
|
357
406
|
for _, rec in sample_manifest.iterrows()
|
|
358
407
|
}
|
|
359
408
|
|
|
409
|
+
by_sample_ball_paths: Dict[str, Dict[int, str]] = {}
|
|
410
|
+
for sample_id, sample_manifest in ball_manifest.groupby("sample_id", sort=False):
|
|
411
|
+
sample_manifest = sample_manifest.sort_values("input_index")
|
|
412
|
+
by_sample_ball_paths[str(sample_id)] = {
|
|
413
|
+
int(rec["input_index"]): str(rec["resolved_path"])
|
|
414
|
+
for _, rec in sample_manifest.iterrows()
|
|
415
|
+
}
|
|
416
|
+
|
|
360
417
|
def _resolved_payload_for_row(row: pd.Series) -> str:
|
|
361
418
|
payload = maybe_json_loads(row.get("sample_payload"), {})
|
|
362
419
|
if not isinstance(payload, dict):
|
|
@@ -368,6 +425,7 @@ def convert_json_to_parquet(
|
|
|
368
425
|
return json_dumps_compact(payload_copy)
|
|
369
426
|
|
|
370
427
|
resolved_by_index = by_sample_input_paths.get(str(row.get("sample_id")), {})
|
|
428
|
+
resolved_ball_by_index = by_sample_ball_paths.get(str(row.get("sample_id")), {})
|
|
371
429
|
with_path_idx = 0
|
|
372
430
|
rewritten: List[Any] = []
|
|
373
431
|
for inp in inputs_value:
|
|
@@ -379,6 +437,10 @@ def convert_json_to_parquet(
|
|
|
379
437
|
resolved = resolved_by_index.get(with_path_idx)
|
|
380
438
|
if resolved is not None:
|
|
381
439
|
inp_copy["path"] = resolved
|
|
440
|
+
if inp_copy.get("ball_path"):
|
|
441
|
+
resolved_ball = resolved_ball_by_index.get(with_path_idx)
|
|
442
|
+
if resolved_ball is not None:
|
|
443
|
+
inp_copy["ball_path"] = resolved_ball
|
|
382
444
|
with_path_idx += 1
|
|
383
445
|
rewritten.append(inp_copy)
|
|
384
446
|
payload_copy["inputs"] = rewritten
|
{opensportslib-0.3.0.dev19 → opensportslib-0.3.0.dev21}/opensportslib/tools/parquet_to_osl_json.py
RENAMED
|
@@ -52,18 +52,23 @@ def _extract_sample_media_from_tar(
|
|
|
52
52
|
sample_index: int,
|
|
53
53
|
output_media_root: Path,
|
|
54
54
|
original_paths: List[str],
|
|
55
|
+
original_ball_paths: Optional[List[Optional[str]]] = None,
|
|
55
56
|
overwrite: bool = False,
|
|
56
57
|
) -> int:
|
|
57
58
|
"""
|
|
58
|
-
Extract all input files for *sample_index* from the shard
|
|
59
|
+
Extract all input files for *sample_index* from the shard, including any
|
|
60
|
+
sidecar ball-tracking files (tar members named ``<key>.<idx>.ball.<ext>``).
|
|
59
61
|
|
|
60
|
-
Files are written to ``output_media_root / original_path
|
|
61
|
-
|
|
62
|
+
Files are written to ``output_media_root / original_path`` (or
|
|
63
|
+
``output_media_root / original_ball_path`` for ball members), preserving
|
|
64
|
+
the original relative path structure so that ``inputs[].path`` /
|
|
65
|
+
``inputs[].ball_path`` values stay valid.
|
|
62
66
|
|
|
63
67
|
Returns the number of files extracted.
|
|
64
68
|
"""
|
|
65
69
|
key_prefix = f"{sample_index:09d}."
|
|
66
70
|
extracted = 0
|
|
71
|
+
ball_paths = original_ball_paths or []
|
|
67
72
|
|
|
68
73
|
with tarfile.open(tar_path, "r") as tar:
|
|
69
74
|
members = [
|
|
@@ -72,20 +77,27 @@ def _extract_sample_media_from_tar(
|
|
|
72
77
|
if m.isfile() and m.name.startswith(key_prefix) and not m.name.endswith(".json")
|
|
73
78
|
]
|
|
74
79
|
|
|
75
|
-
def
|
|
76
|
-
|
|
80
|
+
def _member_info(m: tarfile.TarInfo) -> tuple[int, bool]:
|
|
81
|
+
parts = m.name[len(key_prefix) :].split(".")
|
|
77
82
|
try:
|
|
78
|
-
|
|
83
|
+
idx = int(parts[0])
|
|
79
84
|
except ValueError:
|
|
80
|
-
|
|
85
|
+
idx = 0
|
|
86
|
+
is_ball = len(parts) >= 3 and parts[1] == "ball"
|
|
87
|
+
return idx, is_ball
|
|
81
88
|
|
|
82
|
-
members.sort(key=
|
|
89
|
+
members.sort(key=_member_info)
|
|
83
90
|
|
|
84
91
|
for member in members:
|
|
85
|
-
input_idx =
|
|
86
|
-
if
|
|
87
|
-
|
|
88
|
-
|
|
92
|
+
input_idx, is_ball = _member_info(member)
|
|
93
|
+
if is_ball:
|
|
94
|
+
if input_idx >= len(ball_paths) or not ball_paths[input_idx]:
|
|
95
|
+
continue
|
|
96
|
+
out_path = output_media_root / ball_paths[input_idx]
|
|
97
|
+
else:
|
|
98
|
+
if input_idx >= len(original_paths):
|
|
99
|
+
continue
|
|
100
|
+
out_path = output_media_root / original_paths[input_idx]
|
|
89
101
|
if out_path.exists() and not overwrite:
|
|
90
102
|
extracted += 1
|
|
91
103
|
continue
|
|
@@ -230,16 +242,20 @@ def convert_parquet_to_json(
|
|
|
230
242
|
|
|
231
243
|
if extract_media:
|
|
232
244
|
inputs = sample.get("inputs", []) if isinstance(sample, dict) else []
|
|
233
|
-
original_input_paths = [
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if isinstance(inp, dict)
|
|
237
|
-
|
|
245
|
+
original_input_paths: List[str] = []
|
|
246
|
+
original_ball_paths: List[Optional[str]] = []
|
|
247
|
+
for inp in inputs:
|
|
248
|
+
if not isinstance(inp, dict) or not inp.get("path"):
|
|
249
|
+
continue
|
|
250
|
+
original_input_paths.append(str(inp["path"]))
|
|
251
|
+
ball_path = inp.get("ball_path")
|
|
252
|
+
original_ball_paths.append(str(ball_path) if ball_path else None)
|
|
238
253
|
extracted_media_count += _extract_sample_media_from_tar(
|
|
239
254
|
tar_path=tar_path,
|
|
240
255
|
sample_index=sample_index,
|
|
241
256
|
output_media_root=output_media_root_path,
|
|
242
257
|
original_paths=original_input_paths,
|
|
258
|
+
original_ball_paths=original_ball_paths,
|
|
243
259
|
overwrite=overwrite_media,
|
|
244
260
|
)
|
|
245
261
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opensportslib
|
|
3
|
-
Version: 0.3.0.
|
|
3
|
+
Version: 0.3.0.dev21
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "opensportslib"
|
|
7
|
-
version = "0.3.0.
|
|
7
|
+
version = "0.3.0.dev21"
|
|
8
8
|
description = "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."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -12,11 +12,14 @@ from opensportslib.tools.hf_transfer import (
|
|
|
12
12
|
create_dataset_repo_on_hf,
|
|
13
13
|
dataset_repo_exists_on_hf,
|
|
14
14
|
download_dataset_split_from_hf,
|
|
15
|
+
download_dataset_splits_from_hf,
|
|
15
16
|
extract_local_input_upload_entries_from_json,
|
|
16
17
|
extract_repo_paths_from_json,
|
|
17
18
|
is_hf_download_url_not_found_error,
|
|
18
19
|
is_hf_repo_not_found_error,
|
|
19
20
|
is_hf_revision_not_found_error,
|
|
21
|
+
list_dataset_branches_on_hf,
|
|
22
|
+
list_dataset_splits_on_hf,
|
|
20
23
|
read_hf_source_metadata_from_dataset,
|
|
21
24
|
upload_dataset_as_parquet_to_hf,
|
|
22
25
|
upload_dataset_inputs_from_json_to_hf,
|
|
@@ -226,6 +229,164 @@ def test_dataset_repo_exists_on_hf_returns_false_for_repo_not_found(monkeypatch)
|
|
|
226
229
|
assert dataset_repo_exists_on_hf("OpenSportsLab/missing-repo", token="hf_token") is False
|
|
227
230
|
|
|
228
231
|
|
|
232
|
+
def test_list_dataset_branches_on_hf_puts_main_first_then_alphabetical(monkeypatch):
|
|
233
|
+
class _FakeRefs:
|
|
234
|
+
branches = [
|
|
235
|
+
type("_Ref", (), {"name": "zeta"})(),
|
|
236
|
+
type("_Ref", (), {"name": "main"})(),
|
|
237
|
+
type("_Ref", (), {"name": "alpha"})(),
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
class _FakeApi:
|
|
241
|
+
def __init__(self, token=None):
|
|
242
|
+
pass
|
|
243
|
+
|
|
244
|
+
def list_repo_refs(self, repo_id, repo_type=None):
|
|
245
|
+
return _FakeRefs()
|
|
246
|
+
|
|
247
|
+
monkeypatch.setattr(
|
|
248
|
+
"opensportslib.tools.hf_transfer._import_hf_hub",
|
|
249
|
+
lambda: (_FakeApi, object(), object()),
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
assert list_dataset_branches_on_hf("OpenSportsLab/repo") == ["main", "alpha", "zeta"]
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def test_list_dataset_splits_on_hf_detects_parquet_layout(monkeypatch):
|
|
256
|
+
class _FakeApi:
|
|
257
|
+
def __init__(self, token=None):
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
def list_repo_files(self, repo_id, revision=None, repo_type=None):
|
|
261
|
+
return [
|
|
262
|
+
"README.md",
|
|
263
|
+
"train/metadata.parquet",
|
|
264
|
+
"train/shard_manifest.parquet",
|
|
265
|
+
"train/shards/shard-000000.tar",
|
|
266
|
+
"test/metadata.parquet",
|
|
267
|
+
"test/shards/shard-000000.tar",
|
|
268
|
+
]
|
|
269
|
+
|
|
270
|
+
monkeypatch.setattr(
|
|
271
|
+
"opensportslib.tools.hf_transfer._import_hf_hub",
|
|
272
|
+
lambda: (_FakeApi, object(), object()),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
result = list_dataset_splits_on_hf("OpenSportsLab/repo", "main")
|
|
276
|
+
|
|
277
|
+
assert result == {"format": "parquet", "splits": ["train", "test"]}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def test_list_dataset_splits_on_hf_treats_json_dataset_with_parquet_media_as_json(monkeypatch):
|
|
281
|
+
"""
|
|
282
|
+
A JSON-format dataset can reference arbitrary media files (e.g. tensor-encoded
|
|
283
|
+
videos serialized as .parquet) inside a folder that happens to share a split's
|
|
284
|
+
name. That must not be misdetected as the canonical Parquet+WebDataset export
|
|
285
|
+
layout, which requires `{split}/metadata.parquet` + `{split}/shards/*.tar`.
|
|
286
|
+
Regression test for OpenSportsLab/SNGAR-Action-Spotting-Tracking.
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
class _FakeApi:
|
|
290
|
+
def __init__(self, token=None):
|
|
291
|
+
pass
|
|
292
|
+
|
|
293
|
+
def list_repo_files(self, repo_id, revision=None, repo_type=None):
|
|
294
|
+
return [
|
|
295
|
+
"README.md",
|
|
296
|
+
"train.json",
|
|
297
|
+
"valid.json",
|
|
298
|
+
"test.json",
|
|
299
|
+
"train/videos/10502.parquet",
|
|
300
|
+
"train/videos/10503.parquet",
|
|
301
|
+
"valid/videos/3841.parquet",
|
|
302
|
+
"test/videos/3850.parquet",
|
|
303
|
+
]
|
|
304
|
+
|
|
305
|
+
monkeypatch.setattr(
|
|
306
|
+
"opensportslib.tools.hf_transfer._import_hf_hub",
|
|
307
|
+
lambda: (_FakeApi, object(), object()),
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
result = list_dataset_splits_on_hf("OpenSportsLab/repo", "main")
|
|
311
|
+
|
|
312
|
+
assert result == {"format": "json", "splits": ["train", "valid", "test"]}
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def test_list_dataset_splits_on_hf_detects_json_layout(monkeypatch):
|
|
316
|
+
class _FakeApi:
|
|
317
|
+
def __init__(self, token=None):
|
|
318
|
+
pass
|
|
319
|
+
|
|
320
|
+
def list_repo_files(self, repo_id, revision=None, repo_type=None):
|
|
321
|
+
return [
|
|
322
|
+
"dataset_infos.json",
|
|
323
|
+
"train.json",
|
|
324
|
+
"challenge.json",
|
|
325
|
+
"valid.json",
|
|
326
|
+
]
|
|
327
|
+
|
|
328
|
+
monkeypatch.setattr(
|
|
329
|
+
"opensportslib.tools.hf_transfer._import_hf_hub",
|
|
330
|
+
lambda: (_FakeApi, object(), object()),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
result = list_dataset_splits_on_hf("OpenSportsLab/repo", "main")
|
|
334
|
+
|
|
335
|
+
assert result == {"format": "json", "splits": ["train", "valid", "challenge"]}
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def test_list_dataset_splits_on_hf_returns_none_format_when_nothing_matches(monkeypatch):
|
|
339
|
+
class _FakeApi:
|
|
340
|
+
def __init__(self, token=None):
|
|
341
|
+
pass
|
|
342
|
+
|
|
343
|
+
def list_repo_files(self, repo_id, revision=None, repo_type=None):
|
|
344
|
+
return ["README.md", "dataset_infos.json"]
|
|
345
|
+
|
|
346
|
+
monkeypatch.setattr(
|
|
347
|
+
"opensportslib.tools.hf_transfer._import_hf_hub",
|
|
348
|
+
lambda: (_FakeApi, object(), object()),
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
assert list_dataset_splits_on_hf("OpenSportsLab/repo", "main") == {"format": None, "splits": []}
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def test_download_dataset_splits_from_hf_downloads_each_split_with_prefixed_progress(monkeypatch, tmp_path):
|
|
355
|
+
progress_messages = []
|
|
356
|
+
calls = []
|
|
357
|
+
|
|
358
|
+
def _fake_download_dataset_split_from_hf(repo_id, revision, split, output_dir, **kwargs):
|
|
359
|
+
calls.append((repo_id, revision, split, output_dir))
|
|
360
|
+
kwargs["progress_cb"](f"working on {split}")
|
|
361
|
+
return {"split": split, "json_path": str(tmp_path / f"{split}.json")}
|
|
362
|
+
|
|
363
|
+
monkeypatch.setattr(
|
|
364
|
+
"opensportslib.tools.hf_transfer.download_dataset_split_from_hf",
|
|
365
|
+
_fake_download_dataset_split_from_hf,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
results = download_dataset_splits_from_hf(
|
|
369
|
+
"OpenSportsLab/repo",
|
|
370
|
+
"main",
|
|
371
|
+
["train", "valid"],
|
|
372
|
+
str(tmp_path),
|
|
373
|
+
download_format="json",
|
|
374
|
+
progress_cb=progress_messages.append,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
assert [call[2] for call in calls] == ["train", "valid"]
|
|
378
|
+
assert [result["split"] for result in results] == ["train", "valid"]
|
|
379
|
+
assert progress_messages == [
|
|
380
|
+
"[1/2] train: working on train",
|
|
381
|
+
"[2/2] valid: working on valid",
|
|
382
|
+
]
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def test_download_dataset_splits_from_hf_requires_at_least_one_split():
|
|
386
|
+
with pytest.raises(ValueError):
|
|
387
|
+
download_dataset_splits_from_hf("OpenSportsLab/repo", "main", [], "/tmp/out")
|
|
388
|
+
|
|
389
|
+
|
|
229
390
|
def test_upload_dataset_inputs_from_json_to_hf_uploads_inputs_and_json(monkeypatch, tmp_path):
|
|
230
391
|
clip_path = tmp_path / "train" / "clip_0.mp4"
|
|
231
392
|
clip_path.parent.mkdir(parents=True)
|
|
File without changes
|