sports2d 0.8.6__py3-none-any.whl → 0.8.8__py3-none-any.whl

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.
@@ -151,6 +151,7 @@ filter_type = 'butterworth' # butterworth, gaussian, LOESS, median
151
151
  [kinematics]
152
152
  do_ik = false # Do scaling and inverse kinematics?
153
153
  use_augmentation = false # true or false (lowercase) # Set to true if you want to use the model with augmented markers
154
+ feet_on_floor = false # true or false (lowercase) # Set to false if you want to use the model with feet not on the floor (e.g. running, jumping, etc.)
154
155
  use_contacts_muscles = true # true or false (lowercase) # If true, contact spheres and muscles are added to the model
155
156
  participant_mass = [55.0, 67.0] # kg # defaults to 70 if not provided. No influence on kinematics (motion), only on kinetics (forces)
156
157
  right_left_symmetry = true # true or false (lowercase) # Set to false only if you have good reasons to think the participant is not symmetrical (e.g. prosthetic limb)
Sports2D/Sports2D.py CHANGED
@@ -151,7 +151,44 @@ DEFAULT_CONFIG = {'base': {'video_input': ['demo.mp4'],
151
151
  'deepsort_params': """{'max_age':30, 'n_init':3, 'nms_max_overlap':0.8, 'max_cosine_distance':0.3, 'nn_budget':200, 'max_iou_distance':0.8, 'embedder_gpu': True, 'embedder':'torchreid'}""",
152
152
  'keypoint_likelihood_threshold': 0.3,
153
153
  'average_likelihood_threshold': 0.5,
154
- 'keypoint_number_threshold': 0.3
154
+ 'keypoint_number_threshold': 0.3,
155
+ 'CUSTOM': { 'name': 'Hip',
156
+ 'id': 19,
157
+ 'children': [{'name': 'RHip',
158
+ 'id': 12,
159
+ 'children': [{'name': 'RKnee',
160
+ 'id': 14,
161
+ 'children': [{'name': 'RAnkle',
162
+ 'id': 16,
163
+ 'children': [{'name': 'RBigToe',
164
+ 'id': 21,
165
+ 'children': [{'name': 'RSmallToe', 'id': 23}]},
166
+ {'name': 'RHeel', 'id': 25}]}]}]},
167
+ {'name': 'LHip',
168
+ 'id': 11,
169
+ 'children': [{'name': 'LKnee',
170
+ 'id': 13,
171
+ 'children': [{'name': 'LAnkle',
172
+ 'id': 15,
173
+ 'children': [{'name': 'LBigToe',
174
+ 'id': 20,
175
+ 'children': [{'name': 'LSmallToe', 'id': 22}]},
176
+ {'name': 'LHeel', 'id': 24}]}]}]},
177
+ {'name': 'Neck',
178
+ 'id': 18,
179
+ 'children': [{'name': 'Head',
180
+ 'id': 17,
181
+ 'children': [{'name': 'Nose', 'id': 0}]},
182
+ {'name': 'RShoulder',
183
+ 'id': 6,
184
+ 'children': [{'name': 'RElbow',
185
+ 'id': 8,
186
+ 'children': [{'name': 'RWrist', 'id': 10}]}]},
187
+ {'name': 'LShoulder',
188
+ 'id': 5,
189
+ 'children': [{'name': 'LElbow',
190
+ 'id': 7,
191
+ 'children': [{'name': 'LWrist', 'id': 9}]}]}]}]}
155
192
  },
156
193
  'px_to_meters_conversion': {
157
194
  'to_meters': True,
@@ -205,6 +242,7 @@ DEFAULT_CONFIG = {'base': {'video_input': ['demo.mp4'],
205
242
  },
206
243
  'kinematics':{'do_ik': False,
207
244
  'use_augmentation': False,
245
+ 'feet_on_floor': False,
208
246
  'use_contacts_muscles': True,
209
247
  'participant_mass': [55.0, 67.0],
210
248
  'right_left_symmetry': True,
@@ -244,7 +282,7 @@ CONFIG_HELP = {'config': ["C", "path to a toml configuration file"],
244
282
  'calculate_angles': ["c", "calculate joint and segment angles. true if not specified"],
245
283
  'save_angles': ["A", "save angles as mot files. true if not specified"],
246
284
  'slowmo_factor': ["", "slow-motion factor. For a video recorded at 240 fps and exported to 30 fps, it would be 240/30 = 8. 1 if not specified"],
247
- 'pose_model': ["p", "only body_with_feet is available for now. body_with_feet if not specified"],
285
+ 'pose_model': ["p", "body_with_feet, whole_body_wrist, whole_body, or body. body_with_feet if not specified"],
248
286
  'mode': ["m", 'light, balanced, performance, or a """{dictionary within triple quote}""". balanced if not specified. Use a dictionary to specify your own detection and/or pose estimation models (more about in the documentation).'],
249
287
  'det_frequency': ["f", "run person detection only every N frames, and inbetween track previously detected bounding boxes. keypoint detection is still run on all frames.\n\
250
288
  Equal to or greater than 1, can be as high as you want in simple uncrowded cases. Much faster, but might be less accurate. 1 if not specified: detection runs on all frames"],
@@ -258,6 +296,7 @@ CONFIG_HELP = {'config': ["C", "path to a toml configuration file"],
258
296
  'save_calib': ["", "save calibration file. true if not specified"],
259
297
  'do_ik': ["", "do inverse kinematics. false if not specified"],
260
298
  'use_augmentation': ["", "Use LSTM marker augmentation. false if not specified"],
299
+ 'feet_on_floor': ["", "offset marker augmentation results so that feet are at floor level. true if not specified"],
261
300
  'use_contacts_muscles': ["", "Use model with contact spheres and muscles. false if not specified"],
262
301
  'participant_mass': ["", "mass of the participant in kg or none. Defaults to 70 if not provided. No influence on kinematics (motion), only on kinetics (forces)"],
263
302
  'close_to_zero_speed_m': ["","Sum for all keypoints: about 50 px/frame or 0.2 m/frame"],
Sports2D/process.py CHANGED
@@ -59,8 +59,8 @@ import ast
59
59
  import copy
60
60
  import shutil
61
61
  import os
62
+ import re
62
63
  from importlib.metadata import version
63
- from functools import partial
64
64
  from datetime import datetime
65
65
  import itertools as it
66
66
  from tqdm import tqdm
@@ -75,7 +75,7 @@ import matplotlib.pyplot as plt
75
75
  from matplotlib.widgets import Slider, Button
76
76
  from matplotlib import patheffects
77
77
 
78
- from rtmlib import PoseTracker, BodyWithFeet, Wholebody, Body, Custom
78
+ from rtmlib import PoseTracker, BodyWithFeet, Wholebody, Body, Hand, Custom
79
79
  from deep_sort_realtime.deepsort_tracker import DeepSort
80
80
 
81
81
  from Sports2D.Utilities import filter
@@ -192,6 +192,79 @@ def setup_video(video_file_path, save_vid, vid_output_path):
192
192
  return cap, out_vid, cam_width, cam_height, fps
193
193
 
194
194
 
195
+ def setup_model_class_mode(pose_model, mode, config_dict={}):
196
+ '''
197
+
198
+ '''
199
+
200
+ if pose_model.upper() in ('HALPE_26', 'BODY_WITH_FEET'):
201
+ model_name = 'HALPE_26'
202
+ ModelClass = BodyWithFeet # 26 keypoints(halpe26)
203
+ logging.info(f"Using HALPE_26 model (body and feet) for pose estimation in {mode} mode.")
204
+ elif pose_model.upper() in ('COCO_133', 'WHOLE_BODY', 'WHOLE_BODY_WRIST'):
205
+ model_name = 'COCO_133'
206
+ ModelClass = Wholebody
207
+ logging.info(f"Using COCO_133 model (body, feet, hands, and face) for pose estimation in {mode} mode.")
208
+ elif pose_model.upper() in ('COCO_17', 'BODY'):
209
+ model_name = 'COCO_17'
210
+ ModelClass = Body
211
+ logging.info(f"Using COCO_17 model (body) for pose estimation in {mode} mode.")
212
+ elif pose_model.upper() =='HAND':
213
+ model_name = 'HAND_21'
214
+ ModelClass = Hand
215
+ logging.info(f"Using HAND_21 model for pose estimation in {mode} mode.")
216
+ elif pose_model.upper() =='FACE':
217
+ model_name = 'FACE_106'
218
+ logging.info(f"Using FACE_106 model for pose estimation in {mode} mode.")
219
+ elif pose_model.upper() =='ANIMAL':
220
+ model_name = 'ANIMAL2D_17'
221
+ logging.info(f"Using ANIMAL2D_17 model for pose estimation in {mode} mode.")
222
+ else:
223
+ model_name = pose_model.upper()
224
+ logging.info(f"Using model {model_name} for pose estimation in {mode} mode.")
225
+ try:
226
+ pose_model = eval(model_name)
227
+ except:
228
+ try: # from Config.toml
229
+ from anytree.importer import DictImporter
230
+ model_name = pose_model.upper()
231
+ pose_model = DictImporter().import_(config_dict.get('pose').get(pose_model))
232
+ if pose_model.id == 'None':
233
+ pose_model.id = None
234
+ logging.info(f"Using model {model_name} for pose estimation.")
235
+ except:
236
+ raise NameError(f'{pose_model} not found in skeletons.py nor in Config.toml')
237
+
238
+ # Manually select the models if mode is a dictionary rather than 'lightweight', 'balanced', or 'performance'
239
+ if not mode in ['lightweight', 'balanced', 'performance'] or 'ModelClass' not in locals():
240
+ try:
241
+ from functools import partial
242
+ try:
243
+ mode = ast.literal_eval(mode)
244
+ except: # if within single quotes instead of double quotes when run with sports2d --mode """{dictionary}"""
245
+ mode = mode.strip("'").replace('\n', '').replace(" ", "").replace(",", '", "').replace(":", '":"').replace("{", '{"').replace("}", '"}').replace('":"/',':/').replace('":"\\',':\\')
246
+ mode = re.sub(r'"\[([^"]+)",\s?"([^"]+)\]"', r'[\1,\2]', mode) # changes "[640", "640]" to [640,640]
247
+ mode = json.loads(mode)
248
+ det_class = mode.get('det_class')
249
+ det = mode.get('det_model')
250
+ det_input_size = mode.get('det_input_size')
251
+ pose_class = mode.get('pose_class')
252
+ pose = mode.get('pose_model')
253
+ pose_input_size = mode.get('pose_input_size')
254
+
255
+ ModelClass = partial(Custom,
256
+ det_class=det_class, det=det, det_input_size=det_input_size,
257
+ pose_class=pose_class, pose=pose, pose_input_size=pose_input_size)
258
+ logging.info(f"Using model {model_name} with the following custom parameters: {mode}.")
259
+
260
+ except (json.JSONDecodeError, TypeError):
261
+ logging.warning("Invalid mode. Must be 'lightweight', 'balanced', 'performance', or '''{dictionary}''' of parameters within triple quotes. Make sure input_sizes are within square brackets.")
262
+ logging.warning('Using the default "balanced" mode.')
263
+ mode = 'balanced'
264
+
265
+ return pose_model, ModelClass, mode
266
+
267
+
195
268
  def setup_backend_device(backend='auto', device='auto'):
196
269
  '''
197
270
  Set up the backend and device for the pose tracker based on the availability of hardware acceleration.
@@ -1380,6 +1453,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1380
1453
  use_augmentation = config_dict.get('kinematics').get('use_augmentation')
1381
1454
  participant_masses = config_dict.get('kinematics').get('participant_mass')
1382
1455
  participant_masses = participant_masses if isinstance(participant_masses, list) else [participant_masses]
1456
+ feet_on_floor = config_dict.get('kinematics').get('feet_on_floor')
1383
1457
  fastest_frames_to_remove_percent = config_dict.get('kinematics').get('fastest_frames_to_remove_percent')
1384
1458
  large_hip_knee_angles = config_dict.get('kinematics').get('large_hip_knee_angles')
1385
1459
  trimmed_extrema_percent = config_dict.get('kinematics').get('trimmed_extrema_percent')
@@ -1425,54 +1499,14 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1425
1499
  cv2.namedWindow(f'{video_file} Sports2D', cv2.WINDOW_NORMAL + cv2.WINDOW_KEEPRATIO)
1426
1500
  cv2.setWindowProperty(f'{video_file} Sports2D', cv2.WND_PROP_ASPECT_RATIO, cv2.WINDOW_FULLSCREEN)
1427
1501
 
1502
+
1428
1503
  # Select the appropriate model based on the model_type
1429
- if pose_model.upper() in ('HALPE_26', 'BODY_WITH_FEET'):
1430
- model_name = 'HALPE_26'
1431
- ModelClass = BodyWithFeet # 26 keypoints(halpe26)
1432
- logging.info(f"Using HALPE_26 model (body and feet) for pose estimation.")
1433
- elif pose_model.upper() == 'WHOLE_BODY_WRIST':
1434
- model_name = 'COCO_133_WRIST'
1435
- ModelClass = Wholebody
1436
- logging.info(f"Using COCO_133 model (body, feet, 2 hand points) for pose estimation.")
1437
- elif pose_model.upper() in ('COCO_133', 'WHOLE_BODY'):
1438
- model_name = 'COCO_133'
1439
- ModelClass = Wholebody
1440
- logging.info(f"Using COCO_133 model (body, feet, hands, and face) for pose estimation.")
1441
- elif pose_model.upper() in ('COCO_17', 'BODY'):
1442
- model_name = 'COCO_17'
1443
- ModelClass = Body
1444
- logging.info(f"Using COCO_17 model (body) for pose estimation.")
1445
- else:
1446
- raise ValueError(f"Invalid model_type: {model_name}. Must be 'HALPE_26', 'COCO_133', or 'COCO_17'. Use another network (MMPose, DeepLabCut, OpenPose, AlphaPose, BlazePose...) and convert the output files if you need another model. See documentation.")
1504
+ logging.info('\nEstimating pose...')
1447
1505
  pose_model_name = pose_model
1448
- pose_model = eval(model_name)
1449
-
1450
- # Manually select the models if mode is a dictionary rather than 'lightweight', 'balanced', or 'performance'
1451
- if not mode in ['lightweight', 'balanced', 'performance']:
1452
- try:
1453
- try:
1454
- mode = ast.literal_eval(mode)
1455
- except: # if within single quotes instead of double quotes when run with sports2d --mode """{dictionary}"""
1456
- mode = mode.strip("'").replace('\n', '').replace(" ", "").replace(",", '", "').replace(":", '":"').replace("{", '{"').replace("}", '"}').replace('":"/',':/').replace('":"\\',':\\')
1457
- mode = re.sub(r'"\[([^"]+)",\s?"([^"]+)\]"', r'[\1,\2]', mode) # changes "[640", "640]" to [640,640]
1458
- mode = json.loads(mode)
1459
- det_class = mode.get('det_class')
1460
- det = mode.get('det_model')
1461
- det_input_size = mode.get('det_input_size')
1462
- pose_class = mode.get('pose_class')
1463
- pose = mode.get('pose_model')
1464
- pose_input_size = mode.get('pose_input_size')
1465
-
1466
- ModelClass = partial(Custom,
1467
- det_class=det_class, det=det, det_input_size=det_input_size,
1468
- pose_class=pose_class, pose=pose, pose_input_size=pose_input_size,
1469
- backend=backend, device=device)
1470
-
1471
- except (json.JSONDecodeError, TypeError):
1472
- logging.warning("Invalid mode. Must be 'lightweight', 'balanced', 'performance', or '''{dictionary}''' of parameters within triple quotes. Make sure input_sizes are within square brackets.")
1473
- logging.warning('Using the default "balanced" mode.')
1474
- mode = 'balanced'
1506
+ pose_model, ModelClass, mode = setup_model_class_mode(pose_model, mode, config_dict)
1475
1507
 
1508
+ # Select device and backend
1509
+ backend, device = setup_backend_device(backend=backend, device=device)
1476
1510
 
1477
1511
  # Skip pose estimation or set it up:
1478
1512
  if load_trc_px:
@@ -1503,9 +1537,14 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1503
1537
  # Set up pose tracker
1504
1538
  try:
1505
1539
  pose_tracker = setup_pose_tracker(ModelClass, det_frequency, mode, False, backend, device)
1506
- except:
1507
- logging.error('Error: Pose estimation failed. Check in Config.toml that pose_model and mode are valid.')
1508
- raise ValueError('Error: Pose estimation failed. Check in Config.toml that pose_model and mode are valid.')
1540
+ except: # for multi-threading
1541
+ try:
1542
+ import time
1543
+ time.sleep(3)
1544
+ pose_tracker = setup_pose_tracker(ModelClass, det_frequency, mode, False, backend, device)
1545
+ except:
1546
+ logging.error('Error: Pose estimation failed. Check in Config.toml that pose_model and mode are valid.')
1547
+ raise ValueError('Error: Pose estimation failed. Check in Config.toml that pose_model and mode are valid.')
1509
1548
 
1510
1549
  if tracking_mode not in ['deepsort', 'sports2d']:
1511
1550
  logging.warning(f"Tracking mode {tracking_mode} not recognized. Using sports2d method.")
@@ -1696,6 +1735,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1696
1735
  all_frames_scores_homog = all_frames_scores_homog[...,new_keypoints_ids]
1697
1736
 
1698
1737
  frame_range = [0,frame_count] if video_file == 'webcam' else frame_range
1738
+ print(frame_range)
1699
1739
  all_frames_time = pd.Series(np.linspace(frame_range[0]/fps, frame_range[1]/fps, frame_count-frame_range[0]), name='time')
1700
1740
  if load_trc_px:
1701
1741
  selected_persons = [0]
@@ -2127,7 +2167,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
2127
2167
 
2128
2168
  # Delete person if less than 4 valid frames
2129
2169
  pose_path_person = pose_output_path.parent / (pose_output_path.stem + f'_person{i:02d}.trc')
2130
- all_frames_X_person = pd.DataFrame(all_frames_X_homog[:,i,:], columns=keypoints_names)
2170
+ all_frames_X_person = pd.DataFrame(all_frames_X_homog[:,i,:], columns=new_keypoints_names)
2131
2171
  pose_nan_count = len(np.where(all_frames_X_person.sum(axis=1)==0)[0])
2132
2172
  if frame_count - frame_range[0] - pose_nan_count <= 4:
2133
2173
  # heights_m.append(DEFAULT_HEIGHT)
@@ -2151,7 +2191,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
2151
2191
  Pose2Sim_config_dict['project']['participant_height'] = heights_m
2152
2192
  Pose2Sim_config_dict['project']['participant_mass'] = masses
2153
2193
  Pose2Sim_config_dict['project']['frame_range'] = 'all'
2154
- Pose2Sim_config_dict['markerAugmentation']['feet_on_floor'] = False
2194
+ Pose2Sim_config_dict['markerAugmentation']['feet_on_floor'] = feet_on_floor
2155
2195
  Pose2Sim_config_dict['pose']['pose_model'] = pose_model_name.upper()
2156
2196
  Pose2Sim_config_dict = to_dict(Pose2Sim_config_dict)
2157
2197
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sports2d
3
- Version: 0.8.6
3
+ Version: 0.8.8
4
4
  Summary: Compute 2D human pose and angles from a video or a webcam.
5
5
  Author-email: David Pagnon <contact@david-pagnon.com>
6
6
  Maintainer-email: David Pagnon <contact@david-pagnon.com>
@@ -163,7 +163,8 @@ pip install .
163
163
 
164
164
  #### Full install
165
165
 
166
- > Only needed if you want to run inverse kinematics (`--do_ik True`).
166
+ > **N.B.:** Only needed if you want to run inverse kinematics (`--do_ik True`).\
167
+ > **N.B.:** If you already have a Pose2Sim conda environment, you can skip this step. Just run `conda activate Pose2Sim` and `pip install sports2d`.
167
168
 
168
169
  - Install Anaconda or [Miniconda](https://docs.conda.io/en/latest/miniconda.html):\
169
170
  Open an Anaconda prompt and create a virtual environment:
@@ -424,7 +425,7 @@ sports2d --video_input demo.mp4 other_video.mp4 --time_range 1.2 2.7 0 3.5
424
425
  #### Use a custom pose estimation model:
425
426
  - Retrieve hand motion:
426
427
  ``` cmd
427
- sports2d --pose_model WholeBody
428
+ sports2d --pose_model whole_body
428
429
  ```
429
430
  - Use any custom (deployed) MMPose model
430
431
  ``` cmd
@@ -473,7 +474,7 @@ sports2d --help
473
474
  'calculate_angles': ["c", "calculate joint and segment angles. true if not specified"],
474
475
  'save_angles': ["A", "save angles as mot files. true if not specified"],
475
476
  'slowmo_factor': ["", "slow-motion factor. For a video recorded at 240 fps and exported to 30 fps, it would be 240/30 = 8. 1 if not specified"],
476
- 'pose_model': ["p", "only body_with_feet is available for now. body_with_feet if not specified"],
477
+ 'pose_model': ["p", "body_with_feet, whole_body_wrist, whole_body, or body. body_with_feet if not specified"],
477
478
  'mode': ["m", 'light, balanced, performance, or a """{dictionary within triple quote}""". balanced if not specified. Use a dictionary to specify your own detection and/or pose estimation models (more about in the documentation).'],
478
479
  'det_frequency': ["f", "run person detection only every N frames, and inbetween track previously detected bounding boxes. keypoint detection is still run on all frames.\n\
479
480
  Equal to or greater than 1, can be as high as you want in simple uncrowded cases. Much faster, but might be less accurate. 1 if not specified: detection runs on all frames"],
@@ -487,6 +488,7 @@ sports2d --help
487
488
  'save_calib': ["", "save calibration file. true if not specified"],
488
489
  'do_ik': ["", "do inverse kinematics. false if not specified"],
489
490
  'use_augmentation': ["", "Use LSTM marker augmentation. false if not specified"],
491
+ 'feet_on_floor': ["", "offset marker augmentation results so that feet are at floor level. true if not specified"],
490
492
  'use_contacts_muscles': ["", "Use model with contact spheres and muscles. false if not specified"],
491
493
  'participant_mass': ["", "mass of the participant in kg or none. Defaults to 70 if not provided. No influence on kinematics (motion), only on kinetics (forces)"],
492
494
  'close_to_zero_speed_m': ["","Sum for all keypoints: about 50 px/frame or 0.2 m/frame"],
@@ -568,6 +570,7 @@ Will be much faster, with no impact on accuracy. However, the installation takes
568
570
 
569
571
  2. Finally, install ONNX Runtime with GPU support:
570
572
  ```
573
+ pip uninstall onnxruntime
571
574
  pip install onnxruntime-gpu
572
575
  ```
573
576
 
@@ -9,18 +9,18 @@ Content/paper.md,sha256=8rWSOLrKTysloZv0Fz2lr3nayxtHi7GlFMqwdgDVggY,11333
9
9
  Content/sports2d_blender.gif,sha256=wgMuPRxhja3XtQn76_okGXsNnUT9Thp0pnD36GdW5_E,448786
10
10
  Content/sports2d_opensim.gif,sha256=XP1AcjqhbGcJknXUoNJjPWAwaM9ahZafbDgLWvzKJs4,376656
11
11
  Sports2D/Sports2D.ipynb,sha256=VnOVjIl6ndnCJTT13L4W5qTw4T-TQDF3jt3-wxnXDqM,2427047
12
- Sports2D/Sports2D.py,sha256=EYsXZ3A3VpnsVQmpBSftbHC_iivVbyzst6--q2OOb74,30581
12
+ Sports2D/Sports2D.py,sha256=pe2Xzetj-75OdeD4IXtn5tdD8h_4hiF6CRJm26XK6hQ,33472
13
13
  Sports2D/__init__.py,sha256=BuUkPEdItxlkeqz4dmoiPwZLkgAfABJK3KWQ1ujTGwE,153
14
- Sports2D/process.py,sha256=rgFQlY2ITzIw3V0ghr3Df4xrA73h6Lg1rtRQ8-FNBjc,109863
15
- Sports2D/Demo/Config_demo.toml,sha256=IXrV7URqERHQ6cBJaPq05Yt3i86-u08RnCzC_dn53AM,13852
14
+ Sports2D/process.py,sha256=ANVo0ktVbI0Tza6VAqfHt_D_MuF5wxzE-2O-RB0uQcw,111376
15
+ Sports2D/Demo/Config_demo.toml,sha256=S6QTErGIdUvCB3HoBFqlZKtQmbUU-c2cQYJJSW3HXUk,14003
16
16
  Sports2D/Demo/demo.mp4,sha256=2aZkFxhWR7ESMEtXCT8MGA83p2jmoU2sp1ylQfO3gDk,3968304
17
17
  Sports2D/Utilities/__init__.py,sha256=BuUkPEdItxlkeqz4dmoiPwZLkgAfABJK3KWQ1ujTGwE,153
18
18
  Sports2D/Utilities/common.py,sha256=idMRmesFv5BPX-5g3z5dOVa7SpS_8tNgijvGrOZlR-k,11185
19
19
  Sports2D/Utilities/filter.py,sha256=rfZcqofjllKI_5ovZTKEAmyjOZpB_PzbAJ0P874T8Ak,4973
20
20
  Sports2D/Utilities/tests.py,sha256=XnPNK63AZ_xlJBmFmPJtOMrzssBXIPXFzYd73CPzchI,4740
21
- sports2d-0.8.6.dist-info/licenses/LICENSE,sha256=f4qe3nE0Y7ltJho5w-xAR0jI5PUox5Xl-MsYiY7ZRM8,1521
22
- sports2d-0.8.6.dist-info/METADATA,sha256=YYF67ynJh6JKnPNe17grTBIjREjpcr4b62eIscN7qwk,38116
23
- sports2d-0.8.6.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
24
- sports2d-0.8.6.dist-info/entry_points.txt,sha256=V8dFDIXatz9VvoGgoHzb2wE71C9-f85K6_OjnEQlxww,108
25
- sports2d-0.8.6.dist-info/top_level.txt,sha256=cWWBiDD2WbQXMoIoN6-9et9U2t2c_ZKo2JtBqO5uN-k,17
26
- sports2d-0.8.6.dist-info/RECORD,,
21
+ sports2d-0.8.8.dist-info/licenses/LICENSE,sha256=f4qe3nE0Y7ltJho5w-xAR0jI5PUox5Xl-MsYiY7ZRM8,1521
22
+ sports2d-0.8.8.dist-info/METADATA,sha256=KL-dgSY0VXtZjMfvfDZ0pXcSlZ_tQ9IJTo930_PTRMw,38435
23
+ sports2d-0.8.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
+ sports2d-0.8.8.dist-info/entry_points.txt,sha256=V8dFDIXatz9VvoGgoHzb2wE71C9-f85K6_OjnEQlxww,108
25
+ sports2d-0.8.8.dist-info/top_level.txt,sha256=cWWBiDD2WbQXMoIoN6-9et9U2t2c_ZKo2JtBqO5uN-k,17
26
+ sports2d-0.8.8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5