opensportslib 0.1.2.dev1__tar.gz → 0.1.2.dev3__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.1.2.dev1/opensportslib.egg-info → opensportslib-0.1.2.dev3}/PKG-INFO +70 -11
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/README.md +66 -10
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/examples/quickstart/basic_classification.py +12 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/examples/quickstart/basic_localization.py +12 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/__init__.py +5 -5
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/apis/base_task_model.py +35 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/apis/classification.py +16 -10
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/apis/localization.py +29 -8
- opensportslib-0.1.2.dev3/opensportslib/cli.py +28 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/trainer/classification_trainer.py +4 -4
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/trainer/localization_trainer.py +2 -2
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/utils/config.py +4 -4
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/utils/load_annotations.py +1 -1
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/metrics/localization_metric.py +1 -1
- opensportslib-0.1.2.dev3/opensportslib/tools/__init__.py +49 -0
- opensportslib-0.1.2.dev3/opensportslib/tools/_common.py +28 -0
- opensportslib-0.1.2.dev3/opensportslib/tools/hf_transfer.py +895 -0
- opensportslib-0.1.2.dev3/opensportslib/tools/osl_json_to_parquet.py +411 -0
- opensportslib-0.1.2.dev3/opensportslib/tools/parquet_to_osl_json.py +265 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3/opensportslib.egg-info}/PKG-INFO +70 -11
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib.egg-info/SOURCES.txt +15 -1
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib.egg-info/requires.txt +3 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib.egg-info/top_level.txt +1 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/pyproject.toml +2 -2
- opensportslib-0.1.2.dev3/tests/test_conversion_tools.py +273 -0
- opensportslib-0.1.2.dev3/tests/test_hf_transfer_tools.py +788 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/tests/test_package_smoke.py +1 -0
- opensportslib-0.1.2.dev3/tests/test_task_model_api_contract.py +375 -0
- opensportslib-0.1.2.dev3/tools/convert/osl_json_to_parquet_webdataset.py +104 -0
- opensportslib-0.1.2.dev3/tools/convert/parquet_webdataset_to_osl_json.py +69 -0
- opensportslib-0.1.2.dev3/tools/download/download_hf_repo.py +109 -0
- opensportslib-0.1.2.dev3/tools/download/download_osl_hf.py +120 -0
- opensportslib-0.1.2.dev3/tools/download/upload_osl_hf.py +164 -0
- opensportslib-0.1.2.dev3/tools/training/classification.py +43 -0
- opensportslib-0.1.2.dev3/tools/training/localization.py +43 -0
- opensportslib-0.1.2.dev1/opensportslib/cli.py +0 -22
- opensportslib-0.1.2.dev1/tests/test_task_model_api_contract.py +0 -73
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/LICENSE +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/LICENSE-COMMERCIAL +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/MANIFEST.in +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/apis/__init__.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/config/classification.yaml +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/config/localization-e2e-ocv.yaml +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/config/localization-json_calf_resnetpca512.yaml +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/config/localization-json_netvlad++_resnetpca512.yaml +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/config/localization.yaml +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/config/sngar-frames.yaml +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/config/sngar-tracking.yaml +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/__init__.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/loss/__init__.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/loss/builder.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/loss/calf.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/loss/ce.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/loss/combine.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/loss/nll.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/optimizer/__init__.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/optimizer/builder.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/sampler/weighted_sampler.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/scheduler/__init__.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/scheduler/builder.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/trainer/__init__.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/utils/checkpoint.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/utils/data.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/utils/ddp.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/utils/default_args.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/utils/lightning.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/utils/seed.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/utils/video_processing.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/core/utils/wandb.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/datasets/__init__.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/datasets/builder.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/datasets/classification_dataset.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/datasets/localization_dataset.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/datasets/utils/__init__.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/datasets/utils/tracking.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/metrics/classification_metric.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/__init__.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/backbones/builder.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/base/contextaware.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/base/e2e.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/base/learnablepooling.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/base/tracking.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/base/vars.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/base/video.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/base/video_mae.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/builder.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/heads/builder.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/neck/builder.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/common.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/impl/__init__.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/impl/asformer.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/impl/calf.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/impl/gsm.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/impl/gtad.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/impl/tsm.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/litebase.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/modules.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/shift.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/models/utils/utils.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib/setup/setup.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib.egg-info/dependency_links.txt +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/opensportslib.egg-info/entry_points.txt +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/setup.cfg +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/tests/conftest.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/tests/test_config_utils_smoke.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/tests/test_public_apis_smoke.py +0 -0
- {opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/tests/test_subset_train_infer_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opensportslib
|
|
3
|
-
Version: 0.1.2.
|
|
3
|
+
Version: 0.1.2.dev3
|
|
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
|
|
@@ -24,6 +24,9 @@ Requires-Dist: timm
|
|
|
24
24
|
Requires-Dist: seaborn
|
|
25
25
|
Requires-Dist: tabulate
|
|
26
26
|
Requires-Dist: pytorch-lightning
|
|
27
|
+
Requires-Dist: pandas
|
|
28
|
+
Requires-Dist: pyarrow
|
|
29
|
+
Requires-Dist: huggingface_hub
|
|
27
30
|
Provides-Extra: test
|
|
28
31
|
Requires-Dist: pytest; extra == "test"
|
|
29
32
|
Requires-Dist: pytest-cov; extra == "test"
|
|
@@ -123,14 +126,14 @@ print("OpenSportsLib imported successfully")
|
|
|
123
126
|
### Train a classification model
|
|
124
127
|
|
|
125
128
|
```python
|
|
126
|
-
from opensportslib import
|
|
129
|
+
from opensportslib.apis import ClassificationModel
|
|
127
130
|
|
|
128
|
-
|
|
131
|
+
my_model = ClassificationModel(
|
|
129
132
|
config="/path/to/classification.yaml",
|
|
130
133
|
weights="/path/to/weights.pt", # optional
|
|
131
134
|
)
|
|
132
135
|
|
|
133
|
-
|
|
136
|
+
my_model.train(
|
|
134
137
|
train_set="/path/to/train_annotations.json",
|
|
135
138
|
valid_set="/path/to/valid_annotations.json",
|
|
136
139
|
)
|
|
@@ -139,19 +142,29 @@ myModel.train(
|
|
|
139
142
|
### Run inference
|
|
140
143
|
|
|
141
144
|
```python
|
|
142
|
-
from opensportslib import
|
|
145
|
+
from opensportslib.apis import ClassificationModel
|
|
143
146
|
|
|
144
|
-
|
|
147
|
+
my_model = ClassificationModel(
|
|
145
148
|
config="/path/to/classification.yaml",
|
|
146
149
|
weights="/path/to/weights.pt", # optional
|
|
147
150
|
)
|
|
148
151
|
|
|
149
|
-
predictions =
|
|
152
|
+
predictions = my_model.infer(
|
|
150
153
|
test_set="/path/to/test_annotations.json",
|
|
151
154
|
)
|
|
152
155
|
|
|
153
|
-
|
|
156
|
+
saved_predictions = my_model.save_predictions(
|
|
157
|
+
output_path="/path/to/predictions.json",
|
|
158
|
+
predictions=predictions,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
metrics = my_model.evaluate(
|
|
162
|
+
test_set="/path/to/test_annotations.json",
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
metrics_from_file = my_model.evaluate(
|
|
154
166
|
test_set="/path/to/test_annotations.json",
|
|
167
|
+
predictions=saved_predictions,
|
|
155
168
|
)
|
|
156
169
|
|
|
157
170
|
print(metrics)
|
|
@@ -160,13 +173,58 @@ print(metrics)
|
|
|
160
173
|
### Localization example
|
|
161
174
|
|
|
162
175
|
```python
|
|
163
|
-
from opensportslib import
|
|
176
|
+
from opensportslib.apis import LocalizationModel
|
|
177
|
+
|
|
178
|
+
my_model = LocalizationModel(
|
|
179
|
+
config="/path/to/localization.yaml",
|
|
180
|
+
weights="/path/to/weights.pt", # optional
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
predictions = my_model.infer(
|
|
184
|
+
test_set="/path/to/test_annotations.json",
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
saved_predictions = my_model.save_predictions(
|
|
188
|
+
output_path="/path/to/predictions.json",
|
|
189
|
+
predictions=predictions,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
metrics = my_model.evaluate(
|
|
193
|
+
test_set="/path/to/test_annotations.json",
|
|
194
|
+
)
|
|
164
195
|
|
|
165
|
-
|
|
166
|
-
|
|
196
|
+
metrics_from_file = my_model.evaluate(
|
|
197
|
+
test_set="/path/to/test_annotations.json",
|
|
198
|
+
predictions=saved_predictions,
|
|
199
|
+
)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Hugging Face Dataset Transfer
|
|
206
|
+
|
|
207
|
+
OpenSportsLib provides APIs and scripts for downloading and uploading OSL datasets with Hugging Face.
|
|
208
|
+
|
|
209
|
+
### Python API
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from opensportslib.tools import (
|
|
213
|
+
download_dataset_split_from_hf,
|
|
214
|
+
upload_dataset_inputs_from_json_to_hf,
|
|
215
|
+
upload_dataset_as_parquet_to_hf,
|
|
167
216
|
)
|
|
168
217
|
```
|
|
169
218
|
|
|
219
|
+
### Scripts
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
python tools/download_osl_hf.py --repo-id <org/repo> --revision main --split test --format parquet --output-dir downloaded_data
|
|
223
|
+
python tools/upload_osl_hf.py --repo-id <org/repo> --json-path <local_dataset.json> --split test --revision main
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Downloads are placed under `<output-dir>/<revision>/<split>`.
|
|
227
|
+
|
|
170
228
|
---
|
|
171
229
|
|
|
172
230
|
## What you can do with OpenSportsLib
|
|
@@ -201,6 +259,7 @@ Generate text descriptions for sports events and temporal segments.
|
|
|
201
259
|
Use the README for the fast start, then go deeper through:
|
|
202
260
|
|
|
203
261
|
- Full documentation: https://opensportslab.github.io/opensportslib/
|
|
262
|
+
- High-level API guide: [opensportslib/apis/README.md](opensportslib/apis/README.md)
|
|
204
263
|
- Configuration guide: https://opensportslab.github.io/opensportslib/tni/config-guide/
|
|
205
264
|
- Example configs: [examples/configs/](examples/configs/)
|
|
206
265
|
- Quickstart scripts: [examples/quickstart/](examples/quickstart/)
|
|
@@ -92,14 +92,14 @@ print("OpenSportsLib imported successfully")
|
|
|
92
92
|
### Train a classification model
|
|
93
93
|
|
|
94
94
|
```python
|
|
95
|
-
from opensportslib import
|
|
95
|
+
from opensportslib.apis import ClassificationModel
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
my_model = ClassificationModel(
|
|
98
98
|
config="/path/to/classification.yaml",
|
|
99
99
|
weights="/path/to/weights.pt", # optional
|
|
100
100
|
)
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
my_model.train(
|
|
103
103
|
train_set="/path/to/train_annotations.json",
|
|
104
104
|
valid_set="/path/to/valid_annotations.json",
|
|
105
105
|
)
|
|
@@ -108,19 +108,29 @@ myModel.train(
|
|
|
108
108
|
### Run inference
|
|
109
109
|
|
|
110
110
|
```python
|
|
111
|
-
from opensportslib import
|
|
111
|
+
from opensportslib.apis import ClassificationModel
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
my_model = ClassificationModel(
|
|
114
114
|
config="/path/to/classification.yaml",
|
|
115
115
|
weights="/path/to/weights.pt", # optional
|
|
116
116
|
)
|
|
117
117
|
|
|
118
|
-
predictions =
|
|
118
|
+
predictions = my_model.infer(
|
|
119
119
|
test_set="/path/to/test_annotations.json",
|
|
120
120
|
)
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
saved_predictions = my_model.save_predictions(
|
|
123
|
+
output_path="/path/to/predictions.json",
|
|
124
|
+
predictions=predictions,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
metrics = my_model.evaluate(
|
|
128
|
+
test_set="/path/to/test_annotations.json",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
metrics_from_file = my_model.evaluate(
|
|
123
132
|
test_set="/path/to/test_annotations.json",
|
|
133
|
+
predictions=saved_predictions,
|
|
124
134
|
)
|
|
125
135
|
|
|
126
136
|
print(metrics)
|
|
@@ -129,13 +139,58 @@ print(metrics)
|
|
|
129
139
|
### Localization example
|
|
130
140
|
|
|
131
141
|
```python
|
|
132
|
-
from opensportslib import
|
|
142
|
+
from opensportslib.apis import LocalizationModel
|
|
143
|
+
|
|
144
|
+
my_model = LocalizationModel(
|
|
145
|
+
config="/path/to/localization.yaml",
|
|
146
|
+
weights="/path/to/weights.pt", # optional
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
predictions = my_model.infer(
|
|
150
|
+
test_set="/path/to/test_annotations.json",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
saved_predictions = my_model.save_predictions(
|
|
154
|
+
output_path="/path/to/predictions.json",
|
|
155
|
+
predictions=predictions,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
metrics = my_model.evaluate(
|
|
159
|
+
test_set="/path/to/test_annotations.json",
|
|
160
|
+
)
|
|
133
161
|
|
|
134
|
-
|
|
135
|
-
|
|
162
|
+
metrics_from_file = my_model.evaluate(
|
|
163
|
+
test_set="/path/to/test_annotations.json",
|
|
164
|
+
predictions=saved_predictions,
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Hugging Face Dataset Transfer
|
|
172
|
+
|
|
173
|
+
OpenSportsLib provides APIs and scripts for downloading and uploading OSL datasets with Hugging Face.
|
|
174
|
+
|
|
175
|
+
### Python API
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from opensportslib.tools import (
|
|
179
|
+
download_dataset_split_from_hf,
|
|
180
|
+
upload_dataset_inputs_from_json_to_hf,
|
|
181
|
+
upload_dataset_as_parquet_to_hf,
|
|
136
182
|
)
|
|
137
183
|
```
|
|
138
184
|
|
|
185
|
+
### Scripts
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
python tools/download_osl_hf.py --repo-id <org/repo> --revision main --split test --format parquet --output-dir downloaded_data
|
|
189
|
+
python tools/upload_osl_hf.py --repo-id <org/repo> --json-path <local_dataset.json> --split test --revision main
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Downloads are placed under `<output-dir>/<revision>/<split>`.
|
|
193
|
+
|
|
139
194
|
---
|
|
140
195
|
|
|
141
196
|
## What you can do with OpenSportsLib
|
|
@@ -170,6 +225,7 @@ Generate text descriptions for sports events and temporal segments.
|
|
|
170
225
|
Use the README for the fast start, then go deeper through:
|
|
171
226
|
|
|
172
227
|
- Full documentation: https://opensportslab.github.io/opensportslib/
|
|
228
|
+
- High-level API guide: [opensportslib/apis/README.md](opensportslib/apis/README.md)
|
|
173
229
|
- Configuration guide: https://opensportslab.github.io/opensportslib/tni/config-guide/
|
|
174
230
|
- Example configs: [examples/configs/](examples/configs/)
|
|
175
231
|
- Quickstart scripts: [examples/quickstart/](examples/quickstart/)
|
{opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/examples/quickstart/basic_classification.py
RENAMED
|
@@ -29,6 +29,18 @@ def main():
|
|
|
29
29
|
|
|
30
30
|
print(metrics)
|
|
31
31
|
|
|
32
|
+
saved_predictions = my_model.save_predictions(
|
|
33
|
+
output_path="/path/to/predictions.json",
|
|
34
|
+
predictions=predictions,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
metrics_from_file = my_model.evaluate(
|
|
38
|
+
test_set="/path/to/test_annotations.json",
|
|
39
|
+
predictions=saved_predictions,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
print(metrics_from_file)
|
|
43
|
+
|
|
32
44
|
|
|
33
45
|
if __name__ == "__main__":
|
|
34
46
|
main()
|
{opensportslib-0.1.2.dev1 → opensportslib-0.1.2.dev3}/examples/quickstart/basic_localization.py
RENAMED
|
@@ -29,6 +29,18 @@ def main():
|
|
|
29
29
|
|
|
30
30
|
print(metrics)
|
|
31
31
|
|
|
32
|
+
saved_predictions = my_model.save_predictions(
|
|
33
|
+
output_path="/path/to/predictions.json",
|
|
34
|
+
predictions=predictions,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
metrics_from_file = my_model.evaluate(
|
|
38
|
+
test_set="/path/to/test_annotations.json",
|
|
39
|
+
predictions=saved_predictions,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
print(metrics_from_file)
|
|
43
|
+
|
|
32
44
|
|
|
33
45
|
if __name__ == "__main__":
|
|
34
46
|
main()
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import importlib
|
|
2
|
-
|
|
3
|
-
# from . import metrics
|
|
4
|
-
# from . import datasets
|
|
5
|
-
# from . import core
|
|
2
|
+
|
|
6
3
|
|
|
7
4
|
def __getattr__(name):
|
|
8
5
|
if name == "model":
|
|
@@ -13,6 +10,9 @@ def __getattr__(name):
|
|
|
13
10
|
return importlib.import_module("opensportslib.datasets")
|
|
14
11
|
if name == "core":
|
|
15
12
|
return importlib.import_module("opensportslib.core")
|
|
13
|
+
if name == "tools":
|
|
14
|
+
return importlib.import_module("opensportslib.tools")
|
|
16
15
|
raise AttributeError(f"module 'opensportslib' has no attribute '{name}'")
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
__all__ = ["model", "metrics", "datasets", "core", "tools"]
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
+
import logging
|
|
6
7
|
import os
|
|
7
8
|
import uuid
|
|
8
9
|
from abc import ABC, abstractmethod
|
|
10
|
+
from typing import Any
|
|
9
11
|
|
|
10
12
|
from opensportslib.core.utils.config import expand, load_config_omega
|
|
11
13
|
|
|
@@ -14,6 +16,8 @@ class BaseTaskModel(ABC):
|
|
|
14
16
|
"""Thin shared contract for task-level OpenSportsLib wrappers."""
|
|
15
17
|
|
|
16
18
|
def __init__(self, config=None, weights=None):
|
|
19
|
+
self._configure_logging()
|
|
20
|
+
|
|
17
21
|
if config is None:
|
|
18
22
|
raise ValueError("config path is required")
|
|
19
23
|
|
|
@@ -23,10 +27,29 @@ class BaseTaskModel(ABC):
|
|
|
23
27
|
data_cfg = getattr(self.config, "DATA", None)
|
|
24
28
|
if data_cfg is not None and hasattr(data_cfg, "data_dir"):
|
|
25
29
|
data_cfg.data_dir = expand(data_cfg.data_dir)
|
|
30
|
+
logging.info(f"Data directory: {data_cfg.data_dir}")
|
|
26
31
|
|
|
27
32
|
self.run_id = os.environ.get("RUN_ID") or str(uuid.uuid4())[:8]
|
|
28
33
|
os.environ["RUN_ID"] = self.run_id
|
|
29
34
|
|
|
35
|
+
system_cfg = getattr(self.config, "SYSTEM", None)
|
|
36
|
+
if system_cfg is not None:
|
|
37
|
+
base_save_dir = expand(getattr(system_cfg, "save_dir", None) or "./checkpoints")
|
|
38
|
+
model_cfg = getattr(self.config, "MODEL", None)
|
|
39
|
+
backbone_cfg = getattr(model_cfg, "backbone", None)
|
|
40
|
+
model_name = getattr(backbone_cfg, "type", None) or "model"
|
|
41
|
+
run_save_dir = os.path.join(base_save_dir, model_name, self.run_id)
|
|
42
|
+
self.save_dir = run_save_dir
|
|
43
|
+
system_cfg.save_dir = run_save_dir
|
|
44
|
+
if hasattr(system_cfg, "work_dir"):
|
|
45
|
+
system_cfg.work_dir = run_save_dir
|
|
46
|
+
os.makedirs(run_save_dir, exist_ok=True)
|
|
47
|
+
else:
|
|
48
|
+
self.save_dir = expand("./checkpoints")
|
|
49
|
+
os.makedirs(self.save_dir, exist_ok=True)
|
|
50
|
+
|
|
51
|
+
logging.info(f"Save directory: {self.save_dir}")
|
|
52
|
+
|
|
30
53
|
self.model = None
|
|
31
54
|
self.processor = None
|
|
32
55
|
self.trainer = None
|
|
@@ -36,6 +59,17 @@ class BaseTaskModel(ABC):
|
|
|
36
59
|
if weights is not None:
|
|
37
60
|
self.load_weights(weights=weights)
|
|
38
61
|
|
|
62
|
+
@staticmethod
|
|
63
|
+
def _configure_logging() -> None:
|
|
64
|
+
root_logger = logging.getLogger()
|
|
65
|
+
if not root_logger.handlers:
|
|
66
|
+
logging.basicConfig(
|
|
67
|
+
level=logging.INFO,
|
|
68
|
+
format="%(asctime)s | %(levelname)s | %(message)s",
|
|
69
|
+
)
|
|
70
|
+
elif root_logger.level > logging.INFO:
|
|
71
|
+
root_logger.setLevel(logging.INFO)
|
|
72
|
+
|
|
39
73
|
@abstractmethod
|
|
40
74
|
def load_weights(
|
|
41
75
|
self,
|
|
@@ -70,6 +104,7 @@ class BaseTaskModel(ABC):
|
|
|
70
104
|
self,
|
|
71
105
|
test_set: str | None = None,
|
|
72
106
|
weights: str | None = None,
|
|
107
|
+
predictions: str | dict[str, Any] | None = None,
|
|
73
108
|
use_wandb: bool = True,
|
|
74
109
|
**kwargs,
|
|
75
110
|
) -> dict | str | None:
|
|
@@ -179,6 +179,8 @@ class ClassificationModel(BaseTaskModel):
|
|
|
179
179
|
|
|
180
180
|
del kwargs
|
|
181
181
|
|
|
182
|
+
effective_weights = weights if weights is not None else self.last_loaded_weights
|
|
183
|
+
|
|
182
184
|
world_size = torch.cuda.device_count() or self.config.SYSTEM.GPU
|
|
183
185
|
use_ddp = use_ddp and world_size > 1
|
|
184
186
|
|
|
@@ -198,7 +200,7 @@ class ClassificationModel(BaseTaskModel):
|
|
|
198
200
|
train_set,
|
|
199
201
|
valid_set,
|
|
200
202
|
None,
|
|
201
|
-
|
|
203
|
+
effective_weights,
|
|
202
204
|
use_wandb,
|
|
203
205
|
),
|
|
204
206
|
nprocs=world_size,
|
|
@@ -214,7 +216,7 @@ class ClassificationModel(BaseTaskModel):
|
|
|
214
216
|
return_queue=queue,
|
|
215
217
|
train_set=train_set,
|
|
216
218
|
valid_set=valid_set,
|
|
217
|
-
weights=
|
|
219
|
+
weights=effective_weights,
|
|
218
220
|
use_wandb=use_wandb,
|
|
219
221
|
)
|
|
220
222
|
|
|
@@ -243,6 +245,8 @@ class ClassificationModel(BaseTaskModel):
|
|
|
243
245
|
logging.info("Configuration:")
|
|
244
246
|
logging.info(self.config)
|
|
245
247
|
|
|
248
|
+
effective_weights = weights if weights is not None else self.last_loaded_weights
|
|
249
|
+
|
|
246
250
|
world_size = torch.cuda.device_count()
|
|
247
251
|
use_ddp = use_ddp and world_size > 1
|
|
248
252
|
|
|
@@ -261,7 +265,7 @@ class ClassificationModel(BaseTaskModel):
|
|
|
261
265
|
None,
|
|
262
266
|
None,
|
|
263
267
|
test_set,
|
|
264
|
-
|
|
268
|
+
effective_weights,
|
|
265
269
|
use_wandb,
|
|
266
270
|
),
|
|
267
271
|
nprocs=world_size,
|
|
@@ -275,7 +279,7 @@ class ClassificationModel(BaseTaskModel):
|
|
|
275
279
|
config=self.config,
|
|
276
280
|
return_queue=queue,
|
|
277
281
|
test_set=test_set,
|
|
278
|
-
weights=
|
|
282
|
+
weights=effective_weights,
|
|
279
283
|
use_wandb=use_wandb,
|
|
280
284
|
)
|
|
281
285
|
|
|
@@ -286,6 +290,7 @@ class ClassificationModel(BaseTaskModel):
|
|
|
286
290
|
self,
|
|
287
291
|
test_set=None,
|
|
288
292
|
weights=None,
|
|
293
|
+
predictions=None,
|
|
289
294
|
use_ddp=False,
|
|
290
295
|
use_wandb=True,
|
|
291
296
|
**kwargs,
|
|
@@ -302,12 +307,13 @@ class ClassificationModel(BaseTaskModel):
|
|
|
302
307
|
self.config = resolve_config_omega(self.config)
|
|
303
308
|
logging.info("Configuration:")
|
|
304
309
|
logging.info(self.config)
|
|
305
|
-
predictions
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
310
|
+
if predictions is None:
|
|
311
|
+
predictions = self.infer(
|
|
312
|
+
test_set=test_set,
|
|
313
|
+
weights=weights,
|
|
314
|
+
use_ddp=use_ddp,
|
|
315
|
+
use_wandb=use_wandb,
|
|
316
|
+
)
|
|
311
317
|
|
|
312
318
|
self.trainer = self.trainer or Trainer_Classification(self.config)
|
|
313
319
|
test_data = build_dataset(self.config, test_set, None, split="test")
|
|
@@ -9,6 +9,12 @@ from opensportslib.core.utils.config import expand
|
|
|
9
9
|
class LocalizationModel(BaseTaskModel):
|
|
10
10
|
"""Top-level task wrapper for localization / spotting."""
|
|
11
11
|
|
|
12
|
+
def __init__(self, config=None, weights=None):
|
|
13
|
+
super().__init__(config=config, weights=None)
|
|
14
|
+
if weights is not None:
|
|
15
|
+
self.last_loaded_weights = weights
|
|
16
|
+
self.best_checkpoint = weights
|
|
17
|
+
|
|
12
18
|
def _resolve_split_path(self, split: str, override: str | None = None) -> str:
|
|
13
19
|
if override is not None:
|
|
14
20
|
return expand(override)
|
|
@@ -68,6 +74,11 @@ class LocalizationModel(BaseTaskModel):
|
|
|
68
74
|
if weights is None:
|
|
69
75
|
raise ValueError("`weights` must be provided to load_weights().")
|
|
70
76
|
|
|
77
|
+
model_cfg = getattr(self.config, "MODEL", None)
|
|
78
|
+
original_multi_gpu = getattr(model_cfg, "multi_gpu", None)
|
|
79
|
+
if model_cfg is not None and original_multi_gpu is not None:
|
|
80
|
+
model_cfg.multi_gpu = False
|
|
81
|
+
|
|
71
82
|
device = select_device(self.config.SYSTEM)
|
|
72
83
|
if self.model is None:
|
|
73
84
|
self.model = build_model(self.config, device=device)
|
|
@@ -96,6 +107,9 @@ class LocalizationModel(BaseTaskModel):
|
|
|
96
107
|
self.last_loaded_weights = weights
|
|
97
108
|
self.best_checkpoint = weights
|
|
98
109
|
|
|
110
|
+
if model_cfg is not None and original_multi_gpu is not None:
|
|
111
|
+
model_cfg.multi_gpu = original_multi_gpu
|
|
112
|
+
|
|
99
113
|
def train(
|
|
100
114
|
self,
|
|
101
115
|
train_set=None,
|
|
@@ -137,6 +151,8 @@ class LocalizationModel(BaseTaskModel):
|
|
|
137
151
|
logging.info("Configuration:")
|
|
138
152
|
logging.info(self.config)
|
|
139
153
|
|
|
154
|
+
effective_weights = weights if weights is not None else self.last_loaded_weights
|
|
155
|
+
|
|
140
156
|
def set_seed(seed):
|
|
141
157
|
random.seed(seed)
|
|
142
158
|
np.random.seed(seed)
|
|
@@ -184,7 +200,7 @@ class LocalizationModel(BaseTaskModel):
|
|
|
184
200
|
cfg=self.config,
|
|
185
201
|
model=self.model,
|
|
186
202
|
default_args=get_default_args_trainer(self.config, len(train_loader)),
|
|
187
|
-
resume_from=
|
|
203
|
+
resume_from=effective_weights,
|
|
188
204
|
)
|
|
189
205
|
|
|
190
206
|
logging.info("Start training")
|
|
@@ -245,8 +261,11 @@ class LocalizationModel(BaseTaskModel):
|
|
|
245
261
|
|
|
246
262
|
start = time.time()
|
|
247
263
|
|
|
248
|
-
if weights is not None
|
|
249
|
-
|
|
264
|
+
effective_weights = weights if weights is not None else self.last_loaded_weights
|
|
265
|
+
|
|
266
|
+
if effective_weights is not None:
|
|
267
|
+
if self.model is None or self.last_loaded_weights != effective_weights:
|
|
268
|
+
self.load_weights(weights=effective_weights)
|
|
250
269
|
elif self.model is None:
|
|
251
270
|
device = select_device(self.config.SYSTEM)
|
|
252
271
|
self.model = build_model(self.config, device=device)
|
|
@@ -278,6 +297,7 @@ class LocalizationModel(BaseTaskModel):
|
|
|
278
297
|
self,
|
|
279
298
|
test_set=None,
|
|
280
299
|
weights=None,
|
|
300
|
+
predictions=None,
|
|
281
301
|
use_wandb=True,
|
|
282
302
|
**kwargs,
|
|
283
303
|
):
|
|
@@ -307,11 +327,12 @@ class LocalizationModel(BaseTaskModel):
|
|
|
307
327
|
use_wandb=use_wandb,
|
|
308
328
|
)
|
|
309
329
|
|
|
310
|
-
predictions
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
330
|
+
if predictions is None:
|
|
331
|
+
predictions = self.infer(
|
|
332
|
+
test_set=test_set,
|
|
333
|
+
weights=weights,
|
|
334
|
+
use_wandb=use_wandb,
|
|
335
|
+
)
|
|
315
336
|
|
|
316
337
|
metrics = None
|
|
317
338
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from opensportslib.setup.setup import setup
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main(argv: Optional[list[str]] = None) -> int:
|
|
10
|
+
parser = argparse.ArgumentParser(prog="opensportslib")
|
|
11
|
+
parser.add_argument("command", choices=["setup"])
|
|
12
|
+
parser.add_argument("--pyg", action="store_true")
|
|
13
|
+
parser.add_argument("--dali", action="store_true")
|
|
14
|
+
|
|
15
|
+
args = parser.parse_args(argv)
|
|
16
|
+
|
|
17
|
+
if args.command == "setup":
|
|
18
|
+
setup(
|
|
19
|
+
pyg=args.pyg,
|
|
20
|
+
dali=args.dali,
|
|
21
|
+
)
|
|
22
|
+
return 0
|
|
23
|
+
|
|
24
|
+
return 2
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if __name__ == "__main__":
|
|
28
|
+
raise SystemExit(main())
|
|
@@ -517,7 +517,7 @@ class BaseTrainerClassification:
|
|
|
517
517
|
|
|
518
518
|
logging.info(f"RESULTS Length: {len(results)}")
|
|
519
519
|
logging.info(f"Predictions are stored at : {save_path}")
|
|
520
|
-
with open(save_path, "w") as f:
|
|
520
|
+
with open(save_path, "w", encoding="utf-8") as f:
|
|
521
521
|
json.dump(submission, f, indent=2)
|
|
522
522
|
self.predictions_payload = submission
|
|
523
523
|
|
|
@@ -1018,7 +1018,7 @@ class Trainer_Classification:
|
|
|
1018
1018
|
out_dir = os.path.join(self.config.SYSTEM.save_dir, "final")
|
|
1019
1019
|
os.makedirs(out_dir, exist_ok=True)
|
|
1020
1020
|
out_path = os.path.join(out_dir, "predictions_test_epoch_final.json")
|
|
1021
|
-
with open(out_path, "w") as f:
|
|
1021
|
+
with open(out_path, "w", encoding="utf-8") as f:
|
|
1022
1022
|
json.dump(submission, f, indent=2)
|
|
1023
1023
|
self.predictions_payload = submission
|
|
1024
1024
|
return submission
|
|
@@ -1107,14 +1107,14 @@ class Trainer_Classification:
|
|
|
1107
1107
|
if isinstance(pred_path, dict):
|
|
1108
1108
|
pred_data = pred_path
|
|
1109
1109
|
elif isinstance(pred_path, str):
|
|
1110
|
-
with open(pred_path) as f:
|
|
1110
|
+
with open(pred_path, encoding="utf-8") as f:
|
|
1111
1111
|
pred_data = json.load(f)
|
|
1112
1112
|
else:
|
|
1113
1113
|
raise TypeError(
|
|
1114
1114
|
f"Unsupported predictions type: {type(pred_path).__name__}. Expected dict or str."
|
|
1115
1115
|
)
|
|
1116
1116
|
|
|
1117
|
-
with open(gt_path) as f:
|
|
1117
|
+
with open(gt_path, encoding="utf-8") as f:
|
|
1118
1118
|
gt_data = json.load(f)
|
|
1119
1119
|
|
|
1120
1120
|
gt_dict = {}
|
|
@@ -791,7 +791,7 @@ class Evaluator:
|
|
|
791
791
|
# --------------------------------------------------
|
|
792
792
|
# LOAD GT
|
|
793
793
|
# --------------------------------------------------
|
|
794
|
-
with open(cfg.path) as f:
|
|
794
|
+
with open(cfg.path, encoding="utf-8") as f:
|
|
795
795
|
GT_data = json.load(f)
|
|
796
796
|
|
|
797
797
|
# --------------------------------------------------
|
|
@@ -895,7 +895,7 @@ class Evaluator:
|
|
|
895
895
|
if not os.path.exists(pred_file):
|
|
896
896
|
continue
|
|
897
897
|
|
|
898
|
-
with open(pred_file) as f:
|
|
898
|
+
with open(pred_file, encoding="utf-8") as f:
|
|
899
899
|
pred_data_local = json.load(f)
|
|
900
900
|
|
|
901
901
|
if "data" in pred_data_local:
|
|
@@ -105,11 +105,11 @@ def expand(path):
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
def load_json(fpath):
|
|
108
|
-
with open(fpath) as fp:
|
|
108
|
+
with open(fpath, encoding="utf-8") as fp:
|
|
109
109
|
return json.load(fp)
|
|
110
110
|
|
|
111
111
|
def load_gz_json(fpath):
|
|
112
|
-
with gzip.open(fpath, "rt", encoding="
|
|
112
|
+
with gzip.open(fpath, "rt", encoding="utf-8") as fp:
|
|
113
113
|
return json.load(fp)
|
|
114
114
|
|
|
115
115
|
|
|
@@ -118,12 +118,12 @@ def store_json(fpath, obj, pretty=False):
|
|
|
118
118
|
if pretty:
|
|
119
119
|
kwargs["indent"] = 4
|
|
120
120
|
kwargs["sort_keys"] = False
|
|
121
|
-
with open(fpath, "w") as fp:
|
|
121
|
+
with open(fpath, "w", encoding="utf-8") as fp:
|
|
122
122
|
json.dump(obj, fp, **kwargs)
|
|
123
123
|
|
|
124
124
|
|
|
125
125
|
def store_gz_json(fpath, obj):
|
|
126
|
-
with gzip.open(fpath, "wt", encoding="
|
|
126
|
+
with gzip.open(fpath, "wt", encoding="utf-8") as fp:
|
|
127
127
|
json.dump(obj, fp)
|
|
128
128
|
|
|
129
129
|
|