-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresult_dataset.csv
We can make this file beautiful and searchable if this error is corrected: Illegal quoting in line 2.
1799 lines (1439 loc) · 80.2 KB
/
result_dataset.csv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
fname|prefix|middle|suffix|mid_pred|label|comment|exact_match|chrf|bleu|rouge1|rouge2|rougeL
code_examples\tracks_json_writer.py|"from .abstract_writer import AbstractWriter
import os
import json
import numpy as np
from typing import Any, List
class TracksJsonWriter(AbstractWriter):
""""""
A class to write tracking data to JSON files.
This class handles writing both object tracks and keypoint tracks to separate JSON files.
It ensures that existing data can be appended without losing previous entries.
""""""
def __init__(self, save_dir: str = '', object_fname: str = 'object_tracks',
keypoints_fname: str = 'keypoint_tracks') -> None:
""""""
Initializes the TracksJsonWriter.
Args:
save_dir (str): Directory to save JSON files.
object_fname (str): Filename for object tracks (without extension).
keypoints_fname (str): Filename for keypoint tracks (without extension).
""""""
super().__init__()
self.save_dir = save"|"_dir
self.obj_path = os.path.join(self.save_dir, f'{object_fname}.json')
self.kp_path = os.path."|"join(self.save_dir, f'{keypoints_fname}.json')
if os.path.exists(save_dir):
self._remove_existing_files(files=[self.kp_path, self.obj_path])
else:
os.makedirs(save_dir)
def get_object_tracks_path(self) -> str:
""""""Returns the path for the object tracks JSON file.""""""
return self.obj_path
def get_keypoints_tracks_path(self) -> str:
""""""Returns the path for the keypoint tracks JSON file.""""""
return self.kp_path
def write(self, filename: str, tracks: Any) -> None:
""""""Write tracks to a JSON file.
If the file already exists, new tracks are appended.
Args:
filename (str): The name of the file to save tracks.
tracks (Any): The tracking data to write to"|"_dir
self.object_fname = object_fname
self.keypoints_fname = keypoints_fname
def _remove_existing_files(self, files"|2|Some parts make sense, but mostly incorrect.|False|34.72878886685431|0.1668795940625071|0.3157894736842105|0.1111111111111111|0.2631578947368421
code_examples\club_assigner.py|" os.listdir(self.output_dir) if name.startswith('player')])
def apply_mask(self, image: np.ndarray, green_threshold: float = 0.08) -> np.ndarray:
""""""
Apply a mask to an image based on green color in HSV space.
If the mask covers more than green_threshold of the image, apply the inverse of the mask.
Args:
image (np.ndarray): An image to apply the mask to.
green_threshold (float): Threshold for green color coverage.
Returns:
np.ndarray: The masked image.
""""""
hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# Define the green color range in HSV
lower_green = np.array([36, 25, 25])
upper_green = np.array([86, 2"|"55, 255])
# Create the mask
mask = cv2.inRange(hsv_img, lower_green, upper_green)
# Count the number"|" of masked pixels
total_pixels = image.shape[0] * image.shape[1]
masked_pixels = cv2.countNonZero(cv2.bitwise_not(mask))
mask_percentage = masked_pixels / total_pixels
if mask_percentage > green_threshold:
# Apply inverse mask
return cv2.bitwise_and(image, image, mask=cv2.bitwise_not(mask))
else:
# Apply normal mask
return image
def clustering(self, img: np.ndarray) -> Tuple[int, int, int]:
""""""
Perform K-Means clustering on an image to identify the dominant jersey color.
Args:
img (np.ndarray): The input image.
Returns:
Tuple[int, int, int]: The jersey color in BGR format.
""""""
# Reshape image to 2D array
img_reshape ="|", 25])
# Define the mask
mask = cv2.inRange(hsv_img, lower_green, upper_green)
# Apply mask
"|0|Almost entirely correct. Some parts differ, but the meaning is fully correct.|False|71.5370624431677|0.7559926197136398|0.761904761904762|0.7000000000000001|0.761904761904762
code_examples\ball_to_player_assigner.py|"ball_speed
self.speed_check_frames = speed_check_frames
self.possession_tracker = PossessionTracker(club1, club2)
self.last_possession_frame = None
self.last_player_w_ball = None
self.last_possessing_team = -1
self.ball_exists = False
self.ball_lost_frame = None
self.ball_history = []
self.penalty_point_distance = penalty_point_distance
def is_ball_movement_valid(self, ball_pos: Tuple[float, float], current_frame: int) -> bool:
""""""
Checks if the ball's movement is valid based on its previous position.
Args:
ball_pos (Tuple[float, float]): The current position of the ball (x, y).
current_frame (int): The current frame number.
Returns:
"|" bool: True if the ball movement is valid, False otherwise.
""""""
if not self.ball_history:
return True # No history, so movement is valid
last_ball"|"_pos, last_frame = self.ball_history[-1]
if current_frame - last_frame <= self.speed_check_frames:
distance_moved = point_distance(ball_pos, last_ball_pos)
if distance_moved > self.max_ball_speed:
return False # Movement is too large, likely invalid
return True
def assign(self, tracks: Dict[str, Any], current_frame: int, penalty_point_1_pos: Tuple[float, float], penalty_point_2_pos: Tuple[float, float]) -> Tuple[Dict[str, Any], int]:
""""""
Assigns the ball to the nearest player based on various criteria.
Args:
tracks (Dict[str, Any]): A dictionary containing tracked objects.
current_frame (int): The current frame number.
penalty_point_1_pos (Tuple[float, float"|" bool: True if the ball is moving, False otherwise.
""""""
if self.ball_exists:
return True
if self.last_possession_frame is Non"|1|The comment is entirely correct, but the following code is mostly incorrect.|False|50.86561778029923|0.41172693065828364|0.6250000000000001|0.34782608695652173|0.5833333333333334
code_examples\football_video_processor.py|"()[-1]
possession_club1 = possession[0]
possession_club2 = possession[1]
# Calculate sizes for each possession segment in pixels
club1_width = int(bar_width * possession_club1)
club2_width = int(bar_width * possession_club2)
neutral_width = bar_width - club1_width - club2_width
club1_color = self.club_assigner.club1.player_jersey_color
club2_color = self.club_assigner.club2.player_jersey_color
neutral_color = (128, 128, 128)
# Convert Club Colors from RGB to BGR
club1_color = rgb_bgr_converter(club1_color)
club2_color = rgb_bgr_converter(club2"|"_color)
# Draw club 1's possession (left)
cv2.rectangle(frame, (bar_x, bar_y), (bar_x + club1_"|"width, bar_y + bar_height), club1_color, -1)
# Draw neutral possession (middle)
cv2.rectangle(frame, (bar_x + club1_width, bar_y), (bar_x + club1_width + neutral_width, bar_y + bar_height), neutral_color, -1)
# Draw club 2's possession (right)
cv2.rectangle(frame, (bar_x + club1_width + neutral_width, bar_y), (bar_x + bar_width, bar_y + bar_height), club2_color, -1)
# Draw outline for the entire progress bar
cv2.rectangle(frame, (bar_x, bar_y), (bar_x + bar_width, bar_y + bar_height), (0, 0, 0), 2)
# Calculate the"|"_color)
neutral_color = rgb_bgr_converter(neutral_color)
# Draw the progress bar
cv2.rectangle(frame, (bar_"|2|Some parts make sense, but for the main part of the code, the meaning is not correct.|False|45.80112011738665|0.38870836101538103|0.4210526315789474|0.27777777777777773|0.36842105263157887
code_examples\football_video_processor.py|" np.ndarray:
""""""
Combines the original video frame with the projection of player positions on the field image.
Args:
frame (np.ndarray): The original video frame.
projection_frame (np.ndarray): The projected field image with annotations.
Returns:
np.ndarray: The combined frame.
""""""
# Target canvas size
canvas_width, canvas_height = 1920, 1080
# Get dimensions of the original frame and projection frame
h_frame, w_frame, _ = frame.shape
h_proj, w_proj, _ = projection_frame.shape
# Scale the projection to 70% of its original size
scale_proj = 0.7
new_w_proj = int(w_proj * scale_proj)
new_h_proj = int(h_proj * scale_proj)
projection_resized = cv2"|".resize(projection_frame, (new_w_proj, new_h_proj))
# Create a blank canvas of 1920x1080
combined_"|"frame = np.zeros((canvas_height, canvas_width, 3), dtype=np.uint8)
# Copy the main frame onto the canvas (top-left corner)
combined_frame[:h_frame, :w_frame] = frame
# Set the position for the projection frame at the bottom-middle
x_offset = (canvas_width - new_w_proj) // 2
y_offset = canvas_height - new_h_proj - 25 # 25px margin from bottom
# Blend the projection with 75% visibility (alpha transparency)
alpha = 0.75
overlay = combined_frame[y_offset:y_offset + new_h_proj, x_offset:x_offset + new_w_proj]
cv2.addWeighted(projection_resized, alpha, overlay, 1 - alpha, 0, overlay)
"|".resize(projection_frame, (new_w_proj, new_h_proj))
# Combine the original frame with the projection of player positions on the field image
"|0|Almost entirely correct.|False|59.635185548082816|0.5379065333266195|0.4680851063829787|0.3555555555555555|0.425531914893617
code_examples\football_video_processor.py|" all_tracks['object'] = self.speed_estimator.calculate_speed(
all_tracks['object'], self.frame_num, self.cur_fps
)
# Save tracking information if saving is enabled
if self.save_tracks_dir:
self._save_tracks(all_tracks)
self.frame_num += 1
# Annotate the current frame with the tracking information
annotated_frame = self.annotate(frame, all_tracks)
# Append the annotated frame to the processed frames list
processed_frames.append(annotated_frame)
return processed_frames
def annotate(self, frame: np.ndarray, tracks: Dict) -> np.ndarray:
""""""
Annotates the given frame with analised data
Args:
frame (np.ndarray): The current video frame to be annotated.
tracks (Dict[str, Dict[int, np.ndarray]]): A dictionary containing tracking data"|" for objects and keypoints.
Returns:
np.ndarray: The annotated video frame.
""""""
# Draw the frame number if required
if self.draw_frame_num:"|"
frame = self.frame_num_annotator.annotate(frame, {'frame_num': self.frame_num})
# Annotate the frame with keypoint and object tracking information
frame = self.kp_annotator.annotate(frame, tracks['keypoints'])
frame = self.obj_annotator.annotate(frame, tracks['object'])
# Project the object positions onto the football field image
projection_frame = self.projection_annotator.annotate(self.field_image, tracks['object'])
# Combine the frame and projection into a single canvas
combined_frame = self._combine_frame_projection(frame, projection_frame)
# Annotate possession on the combined frame
combined_frame = self._annotate_possession(combined_frame)
return combined_frame
def _combine_frame_projection(self, frame: np.ndarray, projection_frame: np.ndarray) ->"|"s.
Returns:
np.ndarray: The annotated frame.
""""""
# Get the current frame number
self.frame_num = frame.shape[0]
"|1|Mostly correct, but it did not notice the tab in the suffix so the code would not compile.|False|53.58383677434426|0.3896651261444002|0.5853658536585366|0.30769230769230765|0.5853658536585366
code_examples\club_assigner.py|" img.reshape(-1, 3)
# K-Means clustering
self.kmeans.fit(img_reshape)
# Get Cluster Labels
labels = self.kmeans.labels_
# Reshape the labels into the image shape
cluster_img = labels.reshape(img.shape[0], img.shape[1])
# Get Jersey Color
corners = [cluster_img[0, 0], cluster_img[0, -1], cluster_img[-1, 0], cluster_img[-1, -1]]
bg_cluster = max(set(corners), key=corners.count)
# The other cluster is a player cluster
player_cluster = 1 - bg_cluster
jersey_color_bgr = self.kmeans.cluster_centers_[player_cluster]
return (int(jersey_color_bgr[2]), int(jersey_color_"|"bgr[1]), int(jersey_color_bgr[0]))
def save_player_image(self, img: np.ndarray, player_id: int, is"|"_goalkeeper: bool = False) -> None:
""""""
Save the player's image to the specified directory.
Args:
img (np.ndarray): The image of the player.
player_id (int): The unique identifier for the player.
is_goalkeeper (bool): Flag to indicate if the player is a goalkeeper.
""""""
# Use 'goalkeeper' or 'player' prefix based on is_goalkeeper flag
prefix = 'goalkeeper' if is_goalkeeper else 'player'
filename = os.path.join(self.output_dir, f""{prefix}_{player_id}.png"")
if os.path.exists(filename):
return
cv2.imwrite(filename, img)
print(f""Saved {prefix} image: {filename}"")
# Increment the count of saved images
self.saved_images += 1
def get_jersey_color(self,"|"bgr[1]))
def get_jersey_color_bgr_from_img(self, img: np.ndarray, player_id: int, is"|1|Got some parts correct, but some parts are missing/wrong.|False|67.742832825825|0.6217400375926629|0.8095238095238095|0.6500000000000001|0.761904761904762
code_examples\club_assigner.py|"from .club import Club
import os
from sklearn.cluster import KMeans
import numpy as np
import cv2
from typing import Tuple, Dict, Any, Optional
class ClubAssigner:
def __init__(self, club1: Club, club2: Club, images_to_save: int = 0, images_save_path: Optional[str] = None) -> None:
""""""
Initializes the ClubAssigner with club information and image saving parameters.
Args:
club1 (Club): The first club object.
club2 (Club): The second club object.
images_to_save (int): The number of images to save for analysis.
images_save_path (Optional[str]): The directory path to save images.
""""""
self.club1 = club1
self.club2 = club2
self.model = ClubAssignerModel(self.club1, self."|"club2)
self.club_colors: Dict[str, Any] = {
club1.name: club1.player_jersey_color,
club2.name:"|" club2.player_jersey_color
}
self.goalkeeper_colors: Dict[str, Any] = {
club1.name: club1.goalkeeper_jersey_color,
club2.name: club2.goalkeeper_jersey_color
}
self.kmeans = KMeans(n_clusters=2, init='k-means++', n_init=10, random_state=42)
# Saving images for analysis
self.images_to_save = images_to_save
self.output_dir = images_save_path
if not images_save_path:
images_to_save = 0
self.saved_images = 0
else:
if not os.path.exists(self.output_dir):
os.makedirs(self.output_dir)
self.saved_images = len([name for name in"|"club2)
self.images_to_save = images_to_save
self.images_save_path = images_save_path
def assign_club"|1|Some parts are meaninigfull|False|17.288689334470618|0.13442725522288548|0.20512820512820512|0.10810810810810811|0.20512820512820512
code_examples\projection_annotator.py|"from .abstract_annotator import AbstractAnnotator
from utils import is_color_dark, rgb_bgr_converter
import cv2
import numpy as np
from scipy.spatial import Voronoi
from typing import Dict
class ProjectionAnnotator(AbstractAnnotator):
""""""
Class to annotate projections on a projection image, including Voronoi regions for players (and goalkeepers),
and different markers for ball, players, referees, and goalkeepers.
""""""
def _draw_outline(self, frame: np.ndarray, pos: tuple, shape: str = 'circle', size: int = 10, is_dark: bool = True) -> None:
""""""
Draws a white or black outline around the object based on its color and shape.
Parameters:
frame (np.ndarray): The image on which to draw the outline.
pos (tuple): The ("|"x, y) position of the object.
shape (str): The shape of the outline ('circle', 'square', 'dashed_circle', 'plus').
size (int): The"|" size of the outline.
is_dark (bool): Flag indicating whether the color is dark (determines outline color).
""""""
outline_color = (255, 255, 255) if is_dark else (0, 0, 0)
if shape == 'circle':
cv2.circle(frame, (int(pos[0]), int(pos[1])), radius=size + 2, color=outline_color, thickness=2)
elif shape == 'square':
top_left = (int(pos[0]) - (size + 2), int(pos[1]) - (size + 2))
bottom_right = (int(pos[0]) + (size + 2), int(pos[1]) + (size + 2))
cv2.rectangle(frame, top_left, bottom_right, color=outline_color, thickness"|"pos) position of the outline.
shape (str): The shape of the outline.
size (int): The size of the outline.
is_dark (bool): Fla"|1|Some parts are correct.|False|54.151823930498345|0.424908434506412|0.5652173913043478|0.4545454545454545|0.5652173913043478
code_examples\bbox_utils.py|"from typing import Tuple
def get_bbox_center(bbox: Tuple[float, float, float, float]) -> Tuple[float, float]:
""""""
Calculate the center coordinates of a bounding box.
Args:
bbox (Tuple[float, float, float, float]): The bounding box defined by (x1, y1, x2, y2).
Returns:
Tuple[float, float]: The center coordinates (center_x, center_y) of the bounding box.
""""""
x1, y1, x2, y2 = bbox
return (x1 + x2) / 2, (y1 + y2) / 2
def get_bbox_width(bbox: Tuple[float, float, float, float]) -> float:
""""""
Calculate the width of a bounding box.
Args:
bbox (Tuple[float, float, float, float]): The bounding box defined by"|" (x1, y1, x2, y2).
Returns:
float: The width of the bounding box.
""""""
x1, _, x2, _ = bbox
"|" return x2 - x1
def point_distance(p1: Tuple[float, float], p2: Tuple[float, float]) -> float:
""""""
Calculate the Euclidean distance between two points.
Args:
p1 (Tuple[float, float]): The first point (x1, y1).
p2 (Tuple[float, float]): The second point (x2, y2).
Returns:
float: The distance between the two points.
""""""
return ((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5
def point_coord_diff(p1: Tuple[float, float], p2: Tuple[float, float]) -> Tuple[float, float]:
""""""
Calculate the coordinate differences between two points.
Args:
p1 (Tuple[float, float"|" the (x1, y1, x2, y2).
Returns:
float: The width of the bounding box.
""""""
x1, y1, "|0|Mostly correct, the ending is a bit off.|False|85.19407619097936|0.7982204664176965|0.8571428571428571|0.85|0.8571428571428571
code_examples\object_annotator.py|"
color = rgb_bgr_converter(color)
# Annotate based on object type
if track == 'ball':
frame = self.draw_triangle(frame, item['bbox'], self.ball_annotation_color)
elif track == 'referee':
frame = self.draw_ellipse(frame, item['bbox'], self.referee_annotation_color, track_id, -1, track)
else:
speed = item.get('speed', 0)
frame = self.draw_ellipse(frame, item['bbox'], color, track_id, speed, track)
# If the player has the ball, draw a triangle to indicate it
if 'has_ball' in item and item['has_ball']:
frame = self.draw_triangle(frame, item['bbox'], color)
return frame
def draw_triangle(self, frame: np.ndarray, bbox:"|" Tuple[int, int, int, int],
color: Tuple[int, int, int]) -> np.ndarray:
""""""
Draws a triangle to indicate the ball's position."|"
Args:
frame (np.ndarray): The frame where the triangle will be drawn.
bbox (Tuple[int, int, int, int]): The bounding box of the object.
color (Tuple[int, int, int]): The color of the triangle.
Returns:
np.ndarray: The frame with the triangle drawn on it.
""""""
# Adjust the color for the triangle outline based on the ball color's darkness
color2 = (255, 255, 255) if is_color_dark(color) else (0, 0, 0)
# Get the x and y position of the triangle
y = int(bbox[1])
x, _ = get_bbox_center(bbox)
x = int(x)
# Define the triangle points
points = np.array([
[x, y],
[x - 8, y - 1"|" Tuple[int, int, int, int], color: Tuple[int, int, int, int]):
""""""Draws a triangle on the frame.
Args:
fram"|0|Mostly correct|False|64.1931100766128|0.47576053190766077|0.7142857142857143|0.6|0.7142857142857143
code_examples\ball_to_player_assigner.py|"]): The position of the first penalty point (x, y).
penalty_point_2_pos (Tuple[float, float]): The position of the second penalty point (x, y).
Returns:
Tuple[Dict[str, Any], int]: Updated tracks and the ID of the player with the ball.
""""""
# Copy the tracks to avoid mutating the original data
tracks = tracks.copy()
player_w_ball = -1
valid_ball_tracks = []
best_ball_key = None
to_delete = []
if 'ball' in tracks and tracks['ball']:
self.ball_exists = False
self.ball_lost_frame = current_frame
for ball_key, ball_data in tracks['ball'].items():
ball_pos = ball_data['projection']
ball_bbox_center = get_bbox_center(ball_data['bbox'])
is_"|"near_penalty_point = False
if penalty_point_1_pos is not None:
if point_distance(ball_bbox_center, penalty_point_1_pos)"|" < self.penalty_point_distance:
is_near_penalty_point = True
if penalty_point_2_pos is not None:
if point_distance(ball_bbox_center, penalty_point_2_pos) < self.penalty_point_distance:
is_near_penalty_point = True
if not is_near_penalty_point and self.is_ball_movement_valid(ball_pos, current_frame):
valid_ball_tracks.append((ball_key, ball_pos))
else:
to_delete.append(ball_key)
if valid_ball_tracks:
self.ball_exists = True
min_dis = self.max_ball_distance
best_ball_key, best_ball_pos = None, None
players = {**tracks.get('player', {}), **tracks.get('goalkeeper', {})}
for ball"|"near_penalty_point = False
for ball_pos_2 in valid_ball_tracks:
if ball_pos_2[0] < ball_pos["|1|For the most part makes senese. This is a bit harder input sequence.|False|32.909107857480244|0.25149036624422777|0.3902439024390244|0.15384615384615383|0.3902439024390244
code_examples\video_utils.py|" video or None to skip saving.
batch_size (int, optional): Number of frames to process at once.
skip_seconds (int, optional): Seconds to skip at the beginning of the video.
""""""
from annotation import AbstractVideoProcessor # Lazy import
if processor is not None and not isinstance(processor, AbstractVideoProcessor):
raise ValueError(""The processor must be an instance of AbstractVideoProcessor."")
cap = cv2.VideoCapture(video_source)
if not cap.isOpened():
print(""Error: Could not open video source."")
return
fps = int(cap.get(cv2.CAP_PROP_FPS))
frames_to_skip = int(skip_seconds * fps)
# Skip the first 'frames_to_skip' frames
for _ in range(frames_to_skip):
cap.read() # Simply read and discard the frames
frame_queue = queue."|"Queue(maxsize=100)
processed_queue = queue.Queue(maxsize=100)
stop_event = threading.Event()
def signal_handler("|"signum, frame):
""""""Signal handler to initiate shutdown on interrupt.""""""
print(""Interrupt received, initiating shutdown..."")
stop_event.set()
signal.signal(signal.SIGINT, signal_handler)
def frame_capture_thread() -> None:
""""""Thread to capture frames from the video source.""""""
print(""Starting frame capture"")
frame_count = frames_to_skip # Start counting frames from here
try:
while cap.isOpened() and not stop_event.is_set():
ret, frame = cap.read()
if not ret:
print(""No more frames to capture or end of video"")
break
resized_frame = cv2.resize(frame, (1920, 1080))
frame_queue.put((frame_count, resized_frame))
frame_count += 1
except Exception as e:
print(f""Error in"|"Queue()
stop_event = threading.Event()
def signal_handler(signal, frame):
""""""Signal handler to initiate shutdown on interrupt.""""""
print(""Interrupt received"|1|The meaning is correct|False|48.773019444843214|0.28427903339173927|0.380952380952381|0.3|0.380952380952381
code_examples\projection_annotator.py|" and 'ball'.
Returns:
np.ndarray: The annotated frame.
""""""
frame = frame.copy()
frame = self._draw_voronoi(frame, tracks)
for class_name, track_data in tracks.items():
if class_name != 'ball': # Ball is drawn later
for track_id, track_info in track_data.items():
proj_pos = track_info['projection']
color = track_info.get('club_color', (255, 255, 255))
color = rgb_bgr_converter(color)
is_dark_color = is_color_dark(color)
if class_name in ['player', 'goalkeeper']:
shape = 'square' if class_name == 'goalkeeper' else 'circle'
self._draw_outline(frame, proj_pos, shape=shape,"|" is_dark=is_dark_color)
if track_info.get('has_ball', False):
cv2.circle(frame, (int(proj_pos[0"|"]), int(proj_pos[1])), radius=15, color=(0, 255, 0), thickness=2)
if shape == 'circle':
cv2.circle(frame, (int(proj_pos[0]), int(proj_pos[1])), radius=10, color=color, thickness=-1)
else:
top_left = (int(proj_pos[0]) - 10, int(proj_pos[1]) - 10)
bottom_right = (int(proj_pos[0]) + 10, int(proj_pos[1]) + 10)
cv2.rectangle(frame, top_left, bottom_right, color=color, thickness=-1)
elif class_name == 'referee':
self._draw_outline(frame, proj_pos, shape='dashed_circle', is_dark"|" thickness=2)
elif class_name == 'ball':
cv2.circle(frame, (int(proj_pos[0]), int(proj_pos["|1|Makes sense, but some parts are wrong.|False|45.84878016782165|0.40155930370209536|0.48648648648648646|0.39999999999999997|0.48648648648648646
code_examples\football_video_processor.py|" 1e-6) -> List[np.ndarray]:
""""""
Processes a batch of video frames, detects and tracks objects, assigns ball possession, and annotates the frames.
Args:
frames (List[np.ndarray]): List of video frames.
fps (float): Frames per second of the video, used for speed estimation.
Returns:
List[np.ndarray]: A list of annotated video frames.
""""""
self.cur_fps = max(fps, 1e-6)
# Detect objects and keypoints in all frames
batch_obj_detections = self.obj_tracker.detect(frames)
batch_kp_detections = self.kp_tracker.detect(frames)
processed_frames = []
# Process each frame in the batch
for idx, (frame, object_detection, kp_detection) in enumerate(zip(frames, batch_obj_detections"|", batch_kp_detections)):
# Track detected objects and keypoints
obj_tracks = self.obj_tracker.track(object_detection)
kp_tracks = self."|"kp_tracker.track(kp_detection)
# Assign clubs to players based on their tracked position
obj_tracks = self.club_assigner.assign_clubs(frame, obj_tracks)
all_tracks = {'object': obj_tracks, 'keypoints': kp_tracks}
# Map objects to a top-down view of the field
all_tracks = self.obj_mapper.map(all_tracks)
# Assign the ball to the closest player and calculate speed
all_tracks['object'], _ = self.ball_to_player_assigner.assign(
all_tracks['object'], self.frame_num,
all_tracks['keypoints'].get(8, None), # keypoint for player 1
all_tracks['keypoints'].get(24, None) # keypoint for player 2
)
# Estimate the speed of the tracked objects
"|", batch_kp_detections)):
# Process each frame in the batch
processed_frames.append(frame)
# Calculate the speed of the tracked objects
all"|2|Started good, but the rest is wrong.|False|36.15682005478747|0.19757820084213695|0.2790697674418604|0.1951219512195122|0.2790697674418604
code_examples\video_utils.py|" frame capture: {e}"")
finally:
cap.release()
frame_queue.put(None) # Signal end of capture
print(""Frame capture complete"")
def frame_processing_thread() -> None:
""""""Thread to process frames from the frame queue.""""""
print(""Starting frame processing"")
frame_batch = []
while not stop_event.is_set():
try:
item = frame_queue.get(timeout=1)
if item is None:
print(""No more frames to process"")
if frame_batch:
process_batch(frame_batch)
break
frame_count, frame = item
frame_batch.append((frame_count, frame))
if len(frame_batch) == batch_size:
process_batch(frame_batch)
frame_batch = []
except queue.Empty:
continue
except Exception as e:
print(f""Error in"|" frame processing: {e}"")
processed_queue.put(None) # Signal end of processing
print(""Frame processing complete"")
def process_batch(batch: List[Tuple["|"int, np.ndarray]]) -> None:
""""""
Process a batch of frames and put results in the processed queue.
Args:
batch (List[Tuple[int, np.ndarray]]): List of tuples containing frame count and frame data.
""""""
frames = [frame for _, frame in batch]
try:
processed_batch = processor.process(frames, fps)
for (frame_count, _), processed_frame in zip(batch, processed_batch):
processed_queue.put((frame_count, processed_frame))
except Exception as e:
print(f""Error processing batch: {e}"")
traceback.print_exc()
def frame_display_thread(temp_dir: str) -> None:
""""""Thread to display processed frames.""""""
print(""Starting frame display"")
while not stop_event.is_set():
try:
item = processed_queue.get(timeout=1)"|" frame processing: {e}"")
traceback.print_exc()
def process(
processor: Processor,
batch: List[Tuple[int, np.ndarray]],
fp"|1|Makes sense, but not correct.|False|45.40470880180688|0.24516328545632446|0.4736842105263158|0.2777777777777778|0.4736842105263158
code_examples\football_video_processor.py|" return combined_frame
def _annotate_possession(self, frame: np.ndarray) -> np.ndarray:
""""""
Annotates the possession progress bar on the top-left of the frame.
Args:
frame (np.ndarray): The frame to be annotated.
Returns:
np.ndarray: The annotated frame with possession information.
""""""
frame = frame.copy()
overlay = frame.copy()
# Position and size for the possession overlay (top-left with 20px margin)
overlay_width = 500
overlay_height = 100
gap_x = 20 # 20px from the left
gap_y = 20 # 20px from the top
# Draw background rectangle (black with transparency)
cv2.rectangle(overlay, (gap_x, gap_y), (gap_x + overlay_width"|", gap_y + overlay_height), (0, 0, 0), -1)
alpha = 0.4
frame = cv2.addWeighted(overlay, alpha"|", frame, 1 - alpha, 0)
# Position for possession text
text_x = gap_x + 15
text_y = gap_y + 30
# Display ""Possession"" above the progress bar
cv2.putText(frame, 'Possession:', (text_x, text_y), cv2.FONT_HERSHEY_SIMPLEX, .7, (255, 255, 255), 1)
# Position and size for the possession bar (20px margin)
bar_x = text_x
bar_y = text_y + 25
bar_width = overlay_width - bar_x
bar_height = 15
# Get possession data from the ball-to-player assigner
possession = self.ball_to_player_assigner.get_ball_possessions"|", gap_y), (0, 0, 255), 2)
# Draw overlay rectangle (black with transparency)
cv2.rectangle(overlay"|1|Close, makes a lot of sense, but not correct.|False|30.81296855808484|0.29470691492333945|0.4444444444444444|0.17647058823529413|0.3888888888888889
code_examples\football_video_processor.py|"1_text_x = bar_x + club1_width // 2 - 10 # Center of club 1's possession bar
club1_text_y = bar_y + 35 # 20 pixels below the bar
cv2.putText(frame, possession_club1_text, (club1_text_x, club1_text_y),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2) # Black outline
cv2.putText(frame, possession_club1_text, (club1_text_x, club1_text_y),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, club1_color, 1) # Club 1's color
# Text for club 2
club2_text_"|"x = bar_x + club1_width + neutral_width + club2_width // 2 - 10 # Center of club 2's possession bar
club2"|"_text_y = bar_y + 35 # 20 pixels below the bar
cv2.putText(frame, possession_club2_text, (club2_text_x, club2_text_y),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2) # Black outline
cv2.putText(frame, possession_club2_text, (club2_text_x, club2_text_y),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, club2_color, 1) # Club 2's color
def _save_tracks(self, all_tracks: Dict[str, Dict[int, np.ndarray]]) -> None:
""""""
Saves the tracking information for objects and keypoints to the specified directory.
Args"|"x = bar_x + club2_width // 2 - 10 # Center of club 2's possession bar
club2_text_y = ba"|1|Shares some meaning, but not correct.|False|72.66157760092304|0.7711225022142205|0.8260869565217391|0.8181818181818182|0.8260869565217391
code_examples\ball_to_player_assigner.py|"from utils import point_distance, get_bbox_center
from .possession_tracking import PossessionTracker
from club_assignment import Club
from typing import Dict, Tuple, Any
class BallToPlayerAssigner:
""""""Assigns the ball to a player if it fits the criteria""""""
def __init__(self,
club1: Club,
club2: Club,
max_ball_distance: float = 10.0,
grace_period: float = 4.0,
ball_grace_period: float = 2.0,
fps: int = 30,
max_ball_speed: float = 250.0,
speed_check_frames: int = 5,
penalty_point_distance: float = 15.0) -> None:
""""""
Initializes the BallToPlayerAssigner with necessary parameters.
Args:
club1 ("|"Club): The Club object of the first club.
club2 (Club): The Club object of the second club.
max_ball_distance (float): The maximum distance to consider a player"|" as being able to possess the ball.
grace_period (float): The time in seconds a player retains possession after losing the ball.
ball_grace_period (float): The time in seconds to allow a player to retain possession after the ball detection is lost.
fps (int): Frames per second for the video feed.
max_ball_speed (float): The maximum allowed ball movement in pixels between frames.
speed_check_frames (int): The number of frames to check for ball movement.
penalty_point_distance (float): The distance from the penalty point within which the ball is considered invalid.
""""""
self.max_ball_distance = max_ball_distance
self.grace_period_frames = int(grace_period * fps)
self.ball_grace_period_frames = int(ball_grace_period * fps)
self.max_ball_speed = max_"|"Club): The club that the ball is assigned to.
club2 (Club): The club that the ball is assigned to.
max_ball_distance (float): The maximu"|1|Missed some explainations, but partially correct.|False|49.31718839632407|0.47000080768143077|0.5714285714285714|0.3703703703703704|0.5357142857142857
code_examples\video_utils.py|"import cv2
import os
import glob
import queue
import threading
import tempfile
import time
import signal
import traceback
from typing import List, Tuple, Optional
import numpy as np
def _convert_frames_to_video(frame_dir: str, output_video: str, fps: float, frame_size: Tuple[int, int]) -> None:
""""""
Convert frames in a directory to a video file.
Args:
frame_dir (str): Directory containing frame images.
output_video (str): Path to save the output video.
fps (float): Frames per second for the output video.
frame_size (Tuple[int, int]): Size of the frames as (width, height).
""""""
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
out = cv2.VideoWriter(output_video, fourcc"|", fps, frame_size)
frame_files = sorted(glob.glob(os.path.join(frame_dir, ""*.jpg"")))
frame_count = len(frame_"|"files)
if frame_count <= 0:
out.release()
print(""There are no frames to save"")
return
for filename in frame_files:
img = cv2.imread(filename)
out.write(img)
out.release()
print(f""Video saved as {output_video}"")
def process_video(processor = None, video_source: str = 0, output_video: Optional[str] = ""output.mp4"",
batch_size: int = 30, skip_seconds: int = 0) -> None:
""""""
Process a video file or stream, capturing, processing, and displaying frames.
Args:
processor (AbstractVideoProcessor): Object responsible for processing frames.
video_source (str, optional): Video source (default is ""0"" for webcam).
output_video (Optional[str], optional): Path to save the output"|", fps, frame_size)
for frame_file in glob.glob(os.path.join(frame_dir, ""*.jpg"")):
frame = cv2"|2|Has some similarities, but the meaning is incorrect.|False|68.94047256208589|0.5216726804870612|0.7777777777777778|0.6470588235294118|0.7777777777777778
code_examples\speed_estimator.py|"import math
from collections import deque
from typing import Dict, Any, Tuple
class SpeedEstimator:
""""""Estimates the speed of objects (km/h).""""""
def __init__(self, field_width: int = 528, field_height: int = 352,
real_field_length: float = 100, real_field_width: float = 50,
smoothing_window: int = 5) -> None:
""""""
Initialize the SpeedEstimator with the field dimensions and real-world measurements.
Args:
field_width (int): Width of the field in pixels.
field_height (int): Height of the field in pixels.
real_field_length (float): Real-world length of the field in meters.
real_field_width (float): Real-world width of the field in meters.
smoothing_window (int): Number of"|" frames to consider for speed smoothing.
""""""
self.field_width = field_width
self.field_height = field_height
self.real_field_length = real_"|"field_length # in meters
self.real_field_width = real_field_width # in meters
self.previous_positions: Dict[Any, Tuple[Tuple[float, float], int]] = {}
self.speed_history: Dict[Any, deque] = {}
self.smoothing_window = smoothing_window
# Calculate scaling factors
self.scale_x = real_field_length / field_width
self.scale_y = real_field_width / field_height
# Maximum realistic speed (km/h)
self.max_speed = 40.0
def calculate_speed(self, tracks: Dict[str, Any], frame_number: int, fps: float) -> Dict[str, Any]:
""""""
Calculate the speed of players based on their projections and update the track information.
Args:
tracks (Dict[str, Any]): A dictionary containing"|" smoothing windows to use.
""""""
self.field_width = field_width
self.field_height = field_height
self.real_field_length = rea"|0|A lot of similarities.|False|79.807773058756|0.8047174058421076|0.7804878048780488|0.6666666666666667|0.7317073170731706
code_examples\keypoints_tracker.py|"from tracking.abstract_tracker import AbstractTracker
import cv2
import supervision as sv
from typing import List
from ultralytics.engine.results import Results
import numpy as np
class KeypointsTracker(AbstractTracker):
""""""Detection and Tracking of football field keypoints""""""
def __init__(self, model_path: str, conf: float = 0.1, kp_conf: float = 0.7) -> None:
""""""
Initialize KeypointsTracker for tracking keypoints.
Args:
model_path (str): Model path.
conf (float): Confidence threshold for field detection.
kp_conf (float): Confidence threshold for keypoints.
""""""
super().__init__(model_path, conf) # Call the Tracker base class constructor
self.kp_conf = kp_conf # Keypoint Confidence Threshold
self.tracks = [] # Initialize tracks list
self"|".cur_frame = 0 # Frame counter initialization
self.original_size = (1920, 1080) # Original resolution (1920"|"x1080)