opensportslib 0.1.2.dev11__tar.gz → 0.1.2.dev12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. {opensportslib-0.1.2.dev11/opensportslib.egg-info → opensportslib-0.1.2.dev12}/PKG-INFO +2 -1
  2. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/config/localization.yaml +8 -4
  3. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/config/sngar-frames.yaml +7 -4
  4. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/config/sngar-tracking.yaml +8 -8
  5. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/datasets/classification_dataset.py +1 -1
  6. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/datasets/localization_dataset.py +96 -96
  7. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12/opensportslib.egg-info}/PKG-INFO +2 -1
  8. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib.egg-info/SOURCES.txt +1 -0
  9. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib.egg-info/requires.txt +1 -0
  10. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/pyproject.toml +2 -2
  11. opensportslib-0.1.2.dev12/tests/test_localization_dali_filenames.py +59 -0
  12. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/LICENSE +0 -0
  13. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/LICENSE-COMMERCIAL +0 -0
  14. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/MANIFEST.in +0 -0
  15. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/README.md +0 -0
  16. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/examples/quickstart/basic_classification.py +0 -0
  17. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/examples/quickstart/basic_localization.py +0 -0
  18. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/__init__.py +0 -0
  19. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/apis/__init__.py +0 -0
  20. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/apis/base_task_model.py +0 -0
  21. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/apis/classification.py +0 -0
  22. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/apis/localization.py +0 -0
  23. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/cli.py +0 -0
  24. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/config/classification.yaml +0 -0
  25. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/config/localization-e2e-ocv.yaml +0 -0
  26. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/config/localization-json_calf_resnetpca512.yaml +0 -0
  27. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/config/localization-json_netvlad++_resnetpca512.yaml +0 -0
  28. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/__init__.py +0 -0
  29. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/loss/__init__.py +0 -0
  30. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/loss/builder.py +0 -0
  31. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/loss/calf.py +0 -0
  32. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/loss/ce.py +0 -0
  33. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/loss/combine.py +0 -0
  34. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/loss/nll.py +0 -0
  35. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/optimizer/__init__.py +0 -0
  36. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/optimizer/builder.py +0 -0
  37. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/sampler/weighted_sampler.py +0 -0
  38. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/scheduler/__init__.py +0 -0
  39. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/scheduler/builder.py +0 -0
  40. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/trainer/__init__.py +0 -0
  41. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/trainer/classification_trainer.py +0 -0
  42. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/trainer/localization_trainer.py +0 -0
  43. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/utils/checkpoint.py +0 -0
  44. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/utils/config.py +0 -0
  45. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/utils/data.py +0 -0
  46. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/utils/ddp.py +0 -0
  47. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/utils/default_args.py +0 -0
  48. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/utils/lightning.py +0 -0
  49. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/utils/load_annotations.py +0 -0
  50. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/utils/seed.py +0 -0
  51. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/utils/video_processing.py +0 -0
  52. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/core/utils/wandb.py +0 -0
  53. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/datasets/__init__.py +0 -0
  54. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/datasets/builder.py +0 -0
  55. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/datasets/utils/__init__.py +0 -0
  56. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/datasets/utils/tracking.py +0 -0
  57. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/metrics/classification_metric.py +0 -0
  58. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/metrics/localization_metric.py +0 -0
  59. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/__init__.py +0 -0
  60. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/backbones/builder.py +0 -0
  61. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/base/contextaware.py +0 -0
  62. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/base/e2e.py +0 -0
  63. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/base/learnablepooling.py +0 -0
  64. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/base/tracking.py +0 -0
  65. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/base/vars.py +0 -0
  66. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/base/video.py +0 -0
  67. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/base/video_mae.py +0 -0
  68. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/builder.py +0 -0
  69. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/heads/builder.py +0 -0
  70. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/neck/builder.py +0 -0
  71. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/common.py +0 -0
  72. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/impl/__init__.py +0 -0
  73. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/impl/asformer.py +0 -0
  74. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/impl/calf.py +0 -0
  75. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/impl/gsm.py +0 -0
  76. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/impl/gtad.py +0 -0
  77. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/impl/tsm.py +0 -0
  78. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/litebase.py +0 -0
  79. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/modules.py +0 -0
  80. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/shift.py +0 -0
  81. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/models/utils/utils.py +0 -0
  82. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/setup/setup.py +0 -0
  83. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/tools/__init__.py +0 -0
  84. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/tools/_common.py +0 -0
  85. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/tools/hf_transfer.py +0 -0
  86. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/tools/osl_json_to_parquet.py +0 -0
  87. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib/tools/parquet_to_osl_json.py +0 -0
  88. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib.egg-info/dependency_links.txt +0 -0
  89. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib.egg-info/entry_points.txt +0 -0
  90. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/opensportslib.egg-info/top_level.txt +0 -0
  91. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/setup.cfg +0 -0
  92. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tests/conftest.py +0 -0
  93. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tests/test_classification_dataset_paths.py +0 -0
  94. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tests/test_classification_trainer_dataloader.py +0 -0
  95. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tests/test_config_utils_smoke.py +0 -0
  96. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tests/test_conversion_tools.py +0 -0
  97. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tests/test_hf_transfer_tools.py +0 -0
  98. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tests/test_package_smoke.py +0 -0
  99. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tests/test_public_apis_smoke.py +0 -0
  100. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tests/test_subset_train_infer_integration.py +0 -0
  101. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tests/test_task_model_api_contract.py +0 -0
  102. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tools/convert/build_soccernet_gar.py +0 -0
  103. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tools/convert/build_soccernet_gar_action_spotting.py +0 -0
  104. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tools/convert/osl_json_to_parquet_webdataset.py +0 -0
  105. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tools/convert/parquet_webdataset_to_osl_json.py +0 -0
  106. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tools/download/download_hf_repo.py +0 -0
  107. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tools/download/download_osl_hf.py +0 -0
  108. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tools/download/upload_osl_hf.py +0 -0
  109. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tools/training/classification.py +0 -0
  110. {opensportslib-0.1.2.dev11 → opensportslib-0.1.2.dev12}/tools/training/localization.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensportslib
3
- Version: 0.1.2.dev11
3
+ Version: 0.1.2.dev12
4
4
  Summary: OpenSportsLib is the professional library, designed for advanced video understanding in sports. It provides state-of-the-art tools for action recognition, spotting, retrieval, and captioning, making it ideal for researchers, analysts, and developers working with sports video data.
5
5
  Author: Jeet Vora
6
6
  Requires-Python: >=3.12
@@ -27,6 +27,7 @@ Requires-Dist: pytorch-lightning
27
27
  Requires-Dist: pandas
28
28
  Requires-Dist: pyarrow
29
29
  Requires-Dist: huggingface_hub
30
+ Requires-Dist: easydict
30
31
  Provides-Extra: test
31
32
  Requires-Dist: pytest; extra == "test"
32
33
  Requires-Dist: pytest-cov; extra == "test"
@@ -4,7 +4,7 @@ dali: True
4
4
 
5
5
  DATA:
6
6
  dataset_name: SoccerNet
7
- data_dir: /home/vorajv/opensportslib/SoccerNet/annotations/
7
+ data_dir: /home/giancos/datasets/OpenSportsLab/OSL-SNBAS/224p-2024/
8
8
  classes:
9
9
  - PASS
10
10
  - DRIVE
@@ -37,7 +37,7 @@ DATA:
37
37
  classes: ${DATA.classes}
38
38
  output_map: [data, label]
39
39
  video_path: ${DATA.data_dir}/train/
40
- path: ${DATA.train.video_path}/annotations-2024-224p-train.json
40
+ path: ${DATA.train.video_path}/train.json
41
41
  dataloader:
42
42
  batch_size: 8
43
43
  shuffle: true
@@ -49,10 +49,12 @@ DATA:
49
49
  classes: ${DATA.classes}
50
50
  output_map: [data, label]
51
51
  video_path: ${DATA.data_dir}/valid/
52
- path: ${DATA.valid.video_path}/annotations-2024-224p-valid.json
52
+ path: ${DATA.valid.video_path}/valid.json
53
53
  dataloader:
54
54
  batch_size: 8
55
55
  shuffle: true
56
+ num_workers: 4
57
+ pin_memory: true
56
58
 
57
59
  valid_data_frames:
58
60
  type: VideoGameWithDaliVideo
@@ -64,13 +66,15 @@ DATA:
64
66
  dataloader:
65
67
  batch_size: 4
66
68
  shuffle: false
69
+ num_workers: 4
70
+ pin_memory: true
67
71
 
68
72
  test:
69
73
  type: VideoGameWithDaliVideo
70
74
  classes: ${DATA.classes}
71
75
  output_map: [data, label]
72
76
  video_path: ${DATA.data_dir}/test/
73
- path: ${DATA.test.video_path}/annotations-2024-224p-test.json
77
+ path: ${DATA.test.video_path}/test.json
74
78
  results: results_spotting_test
75
79
  nms_window: 2
76
80
  metric: tight
@@ -8,13 +8,14 @@ TASK: classification
8
8
 
9
9
  DATA:
10
10
  dataset_name: sngar
11
- data_dir: /home/spark_user1/opensportslib/sngar-frames
11
+ data_dir: /home/giancos/datasets/OpenSportsLab/soccernetpro-classification-GAR/frames-parquet
12
12
  data_modality: frames_npy
13
13
  # max_samples: 100 # only used for quick testing
14
14
  num_frames: 16
15
15
  frame_size: [224, 224]
16
16
  train:
17
- path: ${DATA.data_dir}/annotations_train.json
17
+ video_path: ${DATA.data_dir}/train
18
+ path: ${DATA.data_dir}/train.json
18
19
  dataloader:
19
20
  batch_size: 8 # for frozen backbone, use 64
20
21
  # for unfrozen backbone, use 32-16-8 depending on the memory available
@@ -22,13 +23,15 @@ DATA:
22
23
  num_workers: 8
23
24
  pin_memory: true
24
25
  valid:
25
- path: ${DATA.data_dir}/annotations_valid.json
26
+ video_path: ${DATA.data_dir}/valid
27
+ path: ${DATA.data_dir}/valid.json
26
28
  dataloader:
27
29
  batch_size: 8
28
30
  num_workers: 8
29
31
  shuffle: false
30
32
  test:
31
- path: ${DATA.data_dir}/annotations_test.json
33
+ video_path: ${DATA.data_dir}/test
34
+ path: ${DATA.data_dir}/test.json
32
35
  dataloader:
33
36
  batch_size: 8
34
37
  num_workers: 8
@@ -9,7 +9,7 @@ TASK: classification
9
9
  DATA:
10
10
  dataset_name: sngar
11
11
  data_modality: tracking_parquet
12
- data_dir: /home/karkid/opensportslib/tracking-dataset
12
+ data_dir: /home/giancos/datasets/OpenSportsLab/soccernetpro-classification-GAR/tracking-parquet
13
13
  preload_data: false
14
14
  train:
15
15
  type: annotations_train.json
@@ -103,10 +103,10 @@ TRAIN:
103
103
  type: CrossEntropyLoss
104
104
 
105
105
  SYSTEM:
106
- log_dir: ./logs
107
- save_dir: ./checkpoints_tracking
108
- use_seed: true
109
- seed: 42
110
- GPU: 4
111
- device: cuda # auto | cuda | cpu
112
- gpu_id: 0
106
+ log_dir: ./logs
107
+ save_dir: ./checkpoints_tracking
108
+ use_seed: true
109
+ seed: 42
110
+ GPU: 1
111
+ device: cuda # auto | cuda | cpu
112
+ gpu_id: 0
@@ -233,7 +233,7 @@ class VideoDataset(ClassificationDataset):
233
233
  numpy.ndarray of shape (T, H, W, C).
234
234
  """
235
235
  if path.endswith(".npy"):
236
- frames = np.load(path).astype(np.float32) / 255.0
236
+ frames = np.load(os.path.join(self.video_path, path)).astype(np.float32) / 255.0
237
237
  if self.transform is not None:
238
238
  frames = self.transform(frames)
239
239
  mean = np.array([0.485, 0.456, 0.406], dtype=np.float32)
@@ -2,7 +2,6 @@ import os
2
2
  import torch
3
3
  import random
4
4
  from torch.utils.data import Dataset
5
- import tempfile
6
5
  import copy
7
6
  import math
8
7
  import numpy as np
@@ -43,6 +42,36 @@ if DALI_AVAILABLE:
43
42
  else:
44
43
  def dali_pipeline_def(func):
45
44
  return func # dummy decorator
45
+
46
+
47
+ def _build_dali_filenames_and_labels(labels):
48
+ filenames = [video["video"] for video in labels]
49
+ label_indices = list(range(len(labels)))
50
+ return filenames, label_indices
51
+
52
+
53
+ def _dali_frame_num_to_local_frame(frame_num, stride):
54
+ return frame_num // stride + 1
55
+
56
+
57
+ def _count_dali_video_samples(num_frames, clip_len, overlap_len):
58
+ step = clip_len - overlap_len
59
+ if step <= 0:
60
+ raise ValueError("`clip_len - overlap_len` must be strictly positive.")
61
+ return len(range(1, num_frames, step))
62
+
63
+
64
+ def _pad_dali_iterator_size(size, batch_size):
65
+ remainder = size % batch_size
66
+ if remainder == 0:
67
+ return size
68
+ return size + (batch_size - remainder)
69
+
70
+
71
+ def _resolve_dali_video_sample(labels, video_idx, frame_num, stride):
72
+ video_meta = labels[video_idx]
73
+ start = _dali_frame_num_to_local_frame(frame_num, stride)
74
+ return video_meta["path"], start
46
75
 
47
76
  class LocalizationDataset(Dataset):
48
77
  def __init__(self, config, annotations_path=None, processor=None, split="train"):
@@ -941,7 +970,6 @@ if DALI_AVAILABLE:
941
970
  "NVIDIA DALI is required for VideoGameWithDali. "
942
971
  "Install it or use another dataset type."
943
972
  )
944
- import random
945
973
  from opensportslib.core.utils.load_annotations import annotationstoe2eformat
946
974
  from opensportslib.core.utils.video_processing import distribute_elements, _get_deferred_rgb_transform, get_stride
947
975
 
@@ -975,38 +1003,20 @@ if DALI_AVAILABLE:
975
1003
  self.TARGET_WIDTH = TARGET_WIDTH
976
1004
 
977
1005
  self._stride = get_stride(input_fps, extract_fps)
978
-
979
- if is_eval:
980
- nb_clips_per_video = math.ceil(dataset_len / len(self._labels)) * epochs
981
- else:
982
- nb_clips_per_video = math.ceil(dataset_len / len(self._labels)) * epochs
983
-
984
- if mixup:
985
- nb_clips_per_video = nb_clips_per_video * 2
986
-
987
- file_list_txt = ""
988
- for index, video in enumerate(self._labels):
989
- video_path = video["video"]
990
- #print("video_path :", video_path)
991
- # video_path = os.path.join(video_dir, video["video"] + extension)
992
- for _ in range(nb_clips_per_video):
993
- #print(video["num_frames"], (clip_len + 1))
994
- random_start = random.randint(1, video["num_frames"] - (clip_len + 1))
995
- file_list_txt += f"{video_path} {index} {random_start * self._stride} {(random_start+clip_len) * self._stride}\n"
996
-
997
- tf = tempfile.NamedTemporaryFile()
998
- tf.write(str.encode(file_list_txt))
999
- tf.flush()
1006
+ self._filenames, self._video_indices = _build_dali_filenames_and_labels(
1007
+ self._labels
1008
+ )
1000
1009
 
1001
1010
  self.pipes = [
1002
1011
  self.video_pipe(
1003
1012
  batch_size=self.batch_size_per_pipe[index],
1013
+ filenames=self._filenames,
1014
+ labels=self._video_indices,
1004
1015
  sequence_length=self.clip_len,
1005
1016
  stride_dali=self._stride,
1006
- step=-1,
1017
+ step=self._stride,
1007
1018
  num_threads=8,
1008
1019
  device_id=i,
1009
- file_list=tf.name,
1010
1020
  shard_id=index,
1011
1021
  num_shards=len(devices),
1012
1022
  )
@@ -1184,7 +1194,14 @@ if DALI_AVAILABLE:
1184
1194
 
1185
1195
  @dali_pipeline_def
1186
1196
  def video_pipe(
1187
- self, file_list, sequence_length, stride_dali, step, shard_id, num_shards
1197
+ self,
1198
+ filenames,
1199
+ labels,
1200
+ sequence_length,
1201
+ stride_dali,
1202
+ step,
1203
+ shard_id,
1204
+ num_shards,
1188
1205
  ):
1189
1206
  """Construct the pipeline to process a video. This pipeline process a clip with specified arguments such as stride,step and sequence length.
1190
1207
  The first step returns clip of frames with associated labels (index of the clip in the list of clips) and the index of the first frame.
@@ -1192,7 +1209,8 @@ if DALI_AVAILABLE:
1192
1209
  The last step is to construct the list of labels (corresponding to events) corresponding with the extracted frames.
1193
1210
 
1194
1211
  Args:
1195
- file_list (string): Path to the file with a list of <file label [start_frame [end_frame]]> values.
1212
+ filenames (List[string]): Video files passed directly to DALI.
1213
+ labels (List[int]): Video indices associated with filenames.
1196
1214
  sequence_length (int): Frames to load per sequence.
1197
1215
  stride_dali (int): Distance between consecutive frames in the sequence.
1198
1216
  step(int): Frame interval between each sequence.
@@ -1206,14 +1224,14 @@ if DALI_AVAILABLE:
1206
1224
  video, label, frame_num = fn.readers.video_resize(
1207
1225
  device="gpu",
1208
1226
  size=(self.TARGET_HEIGHT, self.TARGET_WIDTH),
1209
- file_list=file_list,
1227
+ filenames=filenames,
1228
+ labels=labels,
1210
1229
  sequence_length=sequence_length,
1211
1230
  random_shuffle=True,
1212
1231
  shard_id=shard_id,
1213
1232
  num_shards=num_shards,
1214
1233
  image_type=types.RGB,
1215
- file_list_include_preceding_frame=True,
1216
- file_list_frame_num=True,
1234
+ file_list_include_preceding_frame=False,
1217
1235
  enable_frame_num=True,
1218
1236
  stride=stride_dali,
1219
1237
  step=step,
@@ -1257,7 +1275,9 @@ if DALI_AVAILABLE:
1257
1275
  labels (np.ndarray): Label array of shape (clip_len,).
1258
1276
  """
1259
1277
  video_meta = self._labels[video_idx]
1260
- base_idx = frame_num // self._stride
1278
+ # DALI frame numbers are 0-based, while localization annotations are
1279
+ # normalized to a 1-based extracted-frame axis.
1280
+ base_idx = _dali_frame_num_to_local_frame(frame_num, self._stride)
1261
1281
  labels = np.zeros(self.clip_len, np.int64)
1262
1282
 
1263
1283
  for event in video_meta["events"]:
@@ -1332,9 +1352,8 @@ if DALI_AVAILABLE:
1332
1352
  "NVIDIA DALI is required for VideoGameWithDali. "
1333
1353
  "Install it or use another dataset type."
1334
1354
  )
1335
- import random
1336
1355
  from opensportslib.core.utils.load_annotations import annotationstoe2eformat, construct_labels
1337
- from opensportslib.core.utils.video_processing import distribute_elements, _get_deferred_rgb_transform, get_stride, get_remaining
1356
+ from opensportslib.core.utils.video_processing import get_stride
1338
1357
  self._src_file = label_file
1339
1358
  # self.infer = False
1340
1359
  if label_file.endswith(".json"):
@@ -1353,70 +1372,38 @@ if DALI_AVAILABLE:
1353
1372
  self.crop_dim = crop_dim
1354
1373
  stride = 1
1355
1374
  self._stride = stride
1375
+ self._stride_dali = stride_dali
1356
1376
  self._flip = flip
1357
1377
  self._multi_crop = multi_crop
1358
1378
  self.batch_size = batch_size // len(devices)
1379
+ self.global_batch_size = batch_size
1359
1380
  self.devices = devices
1360
- self._clips = []
1361
1381
  self.IMAGENET_MEAN = IMAGENET_MEAN
1362
1382
  self.IMAGENET_STD = IMAGENET_STD
1363
1383
  self.TARGET_HEIGHT = TARGET_HEIGHT
1364
1384
  self.TARGET_WIDTH = TARGET_WIDTH
1365
- file_list_txt = ""
1366
- cmp = 0
1367
- for l in self._labels:
1368
- has_clip = False
1369
- for i in range(
1370
- 1,
1371
- l[
1372
- "num_frames"
1373
- ], # Need to ensure that all clips have at least one frame
1374
- (clip_len - overlap_len) * self._stride,
1375
- ):
1376
- if i + clip_len > l["num_frames"]:
1377
- end = l["num_frames_base"]
1378
- else:
1379
- end = (i + clip_len) * stride_dali
1380
- has_clip = True
1381
- self._clips.append((l["path"], l["video"], i))
1382
- # if self.infer:
1383
- # video_path = l["video"]
1384
- # else:
1385
- # video_path = os.path.join(video_dir, l["video"] + extension)
1386
- video_path = l["video"]
1387
- file_list_txt += f"{video_path} {cmp} {i * stride_dali} {end}\n"
1388
- # if cmp2 <5:
1389
- # print(file_list_txt)
1390
- # cmp2+=1
1391
- cmp += 1
1392
- last_video = l["video"]
1393
- last_path = l["path"]
1394
- assert has_clip, l
1395
-
1396
- x = get_remaining(len(self._clips), batch_size)
1397
- for _ in range(x):
1398
- self._clips.append((last_path, last_video, i))
1399
- # if self.infer:
1400
- # video_path = l["video"]
1401
- # else:
1402
- # video_path = os.path.join(video_dir, l["video"] + extension)
1403
- video_path = l["video"]
1404
- file_list_txt += f"{video_path} {cmp} {i * stride_dali} {end}\n"
1405
- cmp += 1
1406
- # print(file_list_txt)
1407
- tf = tempfile.NamedTemporaryFile()
1408
- tf.write(str.encode(file_list_txt))
1409
- tf.flush()
1385
+ self._filenames, self._video_indices = _build_dali_filenames_and_labels(
1386
+ self._labels
1387
+ )
1388
+ clip_count = 0
1389
+ for video in self._labels:
1390
+ num_clips = _count_dali_video_samples(
1391
+ video["num_frames"], self._clip_len, overlap_len
1392
+ )
1393
+ assert num_clips > 0, video
1394
+ clip_count += num_clips
1395
+ iterator_size = _pad_dali_iterator_size(clip_count, self.global_batch_size)
1410
1396
 
1411
1397
  self.pipes = [
1412
1398
  self.video_pipe(
1413
1399
  batch_size=self.batch_size,
1400
+ filenames=self._filenames,
1401
+ labels=self._video_indices,
1414
1402
  sequence_length=self._clip_len,
1415
1403
  stride_dali=stride_dali,
1416
- step=-1,
1404
+ step=(self._clip_len - overlap_len) * stride_dali,
1417
1405
  num_threads=8,
1418
1406
  device_id=i,
1419
- file_list=tf.name,
1420
1407
  shard_id=index,
1421
1408
  num_shards=len(devices),
1422
1409
  )
@@ -1426,21 +1413,25 @@ if DALI_AVAILABLE:
1426
1413
  for pipe in self.pipes:
1427
1414
  pipe.build()
1428
1415
 
1429
- size = len(self._clips)
1430
-
1431
- super().__init__(self.pipes, output_map, size=size)
1416
+ internal_output_map = ["data", "video_idx", "frame_num"]
1417
+ super().__init__(self.pipes, internal_output_map, size=iterator_size)
1432
1418
 
1433
1419
  def __next__(self):
1434
1420
  import cupy
1435
1421
 
1436
1422
  out = super().__next__()
1437
1423
  video_names = []
1438
- starts = cupy.zeros(len(self.devices) * self.batch_size, np.int64)
1424
+ total_samples = sum(batch["video_idx"].shape[0] for batch in out)
1425
+ starts = cupy.zeros(total_samples, np.int64)
1439
1426
  cmp = 0
1440
1427
  for j in range(len(out)):
1441
- for i in range(out[j]["label"].shape[0]):
1442
- video_path, video_name, start = self._clips[out[j]["label"][i]]
1443
- video_names.append(video_path)
1428
+ for i in range(out[j]["video_idx"].shape[0]):
1429
+ video_idx = int(out[j]["video_idx"][i].item())
1430
+ frame_num = int(out[j]["frame_num"][i].item())
1431
+ video_name, start = _resolve_dali_video_sample(
1432
+ self._labels, video_idx, frame_num, self._stride_dali
1433
+ )
1434
+ video_names.append(video_name)
1444
1435
  starts[cmp] = start
1445
1436
  cmp += 1
1446
1437
  return {
@@ -1460,14 +1451,22 @@ if DALI_AVAILABLE:
1460
1451
 
1461
1452
  @dali_pipeline_def
1462
1453
  def video_pipe(
1463
- self, file_list, sequence_length, stride_dali, step, shard_id, num_shards
1454
+ self,
1455
+ filenames,
1456
+ labels,
1457
+ sequence_length,
1458
+ stride_dali,
1459
+ step,
1460
+ shard_id,
1461
+ num_shards,
1464
1462
  ):
1465
1463
  """Construct the pipeline to process a video. This pipeline process a clip with specified arguments such as stride,step and sequence length.
1466
1464
  The first step returns clip of frames with associated labels (index of the clip in the list of clips) and the index of the first frame.
1467
1465
  The second step is the cropping, mirroring (only if non eval) and normalizing the frames.
1468
1466
 
1469
1467
  Args:
1470
- file_list (string): Path to the file with a list of <file label [start_frame [end_frame]]> values.
1468
+ filenames (List[string]): Video files passed directly to DALI.
1469
+ labels (List[int]): Video indices associated with filenames.
1471
1470
  sequence_length (int): Frames to load per sequence.
1472
1471
  stride_dali (int): Distance between consecutive frames in the sequence.
1473
1472
  step(int): Frame interval between each sequence.
@@ -1478,17 +1477,18 @@ if DALI_AVAILABLE:
1478
1477
  video (torch.tensor): The frames processed.
1479
1478
  label : the index of the clip in the list of clips.
1480
1479
  """
1481
- video, label = fn.readers.video_resize(
1480
+ video, video_idx, frame_num = fn.readers.video_resize(
1482
1481
  device="gpu",
1483
1482
  size=(self.TARGET_HEIGHT, self.TARGET_WIDTH),
1484
- file_list=file_list,
1483
+ filenames=filenames,
1484
+ labels=labels,
1485
1485
  sequence_length=sequence_length,
1486
1486
  random_shuffle=False,
1487
1487
  shard_id=shard_id,
1488
1488
  num_shards=num_shards,
1489
1489
  image_type=types.RGB,
1490
- file_list_include_preceding_frame=True,
1491
- file_list_frame_num=True,
1490
+ file_list_include_preceding_frame=False,
1491
+ enable_frame_num=True,
1492
1492
  stride=stride_dali,
1493
1493
  step=step,
1494
1494
  pad_sequences=True,
@@ -1505,7 +1505,7 @@ if DALI_AVAILABLE:
1505
1505
  std=[self.IMAGENET_STD[i] * 255.0 for i in range(len(self.IMAGENET_STD))],
1506
1506
  )
1507
1507
 
1508
- return video, label
1508
+ return video, video_idx, frame_num
1509
1509
 
1510
1510
  def get_dims(video):
1511
1511
  print(video.shape)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensportslib
3
- Version: 0.1.2.dev11
3
+ Version: 0.1.2.dev12
4
4
  Summary: OpenSportsLib is the professional library, designed for advanced video understanding in sports. It provides state-of-the-art tools for action recognition, spotting, retrieval, and captioning, making it ideal for researchers, analysts, and developers working with sports video data.
5
5
  Author: Jeet Vora
6
6
  Requires-Python: >=3.12
@@ -27,6 +27,7 @@ Requires-Dist: pytorch-lightning
27
27
  Requires-Dist: pandas
28
28
  Requires-Dist: pyarrow
29
29
  Requires-Dist: huggingface_hub
30
+ Requires-Dist: easydict
30
31
  Provides-Extra: test
31
32
  Requires-Dist: pytest; extra == "test"
32
33
  Requires-Dist: pytest-cov; extra == "test"
@@ -92,6 +92,7 @@ tests/test_classification_trainer_dataloader.py
92
92
  tests/test_config_utils_smoke.py
93
93
  tests/test_conversion_tools.py
94
94
  tests/test_hf_transfer_tools.py
95
+ tests/test_localization_dali_filenames.py
95
96
  tests/test_package_smoke.py
96
97
  tests/test_public_apis_smoke.py
97
98
  tests/test_subset_train_infer_integration.py
@@ -17,6 +17,7 @@ pytorch-lightning
17
17
  pandas
18
18
  pyarrow
19
19
  huggingface_hub
20
+ easydict
20
21
 
21
22
  [:platform_system != "Darwin" and platform_machine != "arm64" and platform_machine != "aarch64"]
22
23
  decord
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "opensportslib"
7
- version = "0.1.2.dev11"
7
+ version = "0.1.2.dev12"
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"
11
- dependencies = [ "SoccerNet", "av", "decord; platform_system != 'Darwin' and platform_machine != 'arm64' and platform_machine != 'aarch64'", "evaluate", "scikit-learn", "torch", "torchvision", "transformers==4.57.3", "tokenizers==0.22.1", "accelerate", "wandb", "opencv-python", "omegaconf", "timm", "seaborn", "tabulate", "pytorch-lightning", "pandas", "pyarrow", "huggingface_hub",]
11
+ dependencies = [ "SoccerNet", "av", "decord; platform_system != 'Darwin' and platform_machine != 'arm64' and platform_machine != 'aarch64'", "evaluate", "scikit-learn", "torch", "torchvision", "transformers==4.57.3", "tokenizers==0.22.1", "accelerate", "wandb", "opencv-python", "omegaconf", "timm", "seaborn", "tabulate", "pytorch-lightning", "pandas", "pyarrow", "huggingface_hub", "easydict",]
12
12
  [[project.authors]]
13
13
  name = "Jeet Vora"
14
14
 
@@ -0,0 +1,59 @@
1
+ import pytest
2
+
3
+ from opensportslib.datasets.localization_dataset import (
4
+ _build_dali_filenames_and_labels,
5
+ _count_dali_video_samples,
6
+ _dali_frame_num_to_local_frame,
7
+ _pad_dali_iterator_size,
8
+ _resolve_dali_video_sample,
9
+ )
10
+
11
+
12
+ def test_build_dali_filenames_preserves_spaced_paths():
13
+ labels = [
14
+ {"video": "/tmp/with spaces/game one.mp4"},
15
+ {"video": "/tmp/plain/game-two.mp4"},
16
+ ]
17
+
18
+ filenames, label_indices = _build_dali_filenames_and_labels(labels)
19
+
20
+ assert filenames == [
21
+ "/tmp/with spaces/game one.mp4",
22
+ "/tmp/plain/game-two.mp4",
23
+ ]
24
+ assert label_indices == [0, 1]
25
+
26
+
27
+ def test_dali_frame_numbers_translate_to_one_based_local_frames():
28
+ assert _dali_frame_num_to_local_frame(0, 12) == 1
29
+ assert _dali_frame_num_to_local_frame(12, 12) == 2
30
+ assert _dali_frame_num_to_local_frame(24, 12) == 3
31
+
32
+
33
+ def test_count_dali_video_samples_matches_previous_manifest_schedule():
34
+ assert _count_dali_video_samples(10, 4, 1) == len(list(range(1, 10, 3)))
35
+ assert _count_dali_video_samples(101, 100, 50) == len(list(range(1, 101, 50)))
36
+
37
+
38
+ def test_count_dali_video_samples_rejects_non_positive_step():
39
+ with pytest.raises(ValueError):
40
+ _count_dali_video_samples(10, 4, 4)
41
+
42
+
43
+ def test_pad_dali_iterator_size_rounds_up_to_full_batch():
44
+ assert _pad_dali_iterator_size(10, 4) == 12
45
+ assert _pad_dali_iterator_size(12, 4) == 12
46
+
47
+
48
+ def test_resolve_dali_video_sample_uses_relative_path_and_one_based_start():
49
+ labels = [
50
+ {
51
+ "path": "train/match one.mp4",
52
+ "video": "/abs/with spaces/train/match one.mp4",
53
+ }
54
+ ]
55
+
56
+ video_name, start = _resolve_dali_video_sample(labels, 0, 24, 12)
57
+
58
+ assert video_name == "train/match one.mp4"
59
+ assert start == 3