phitran commited on
Commit
041f44a
·
1 Parent(s): 58ae0fd

inital commit

Browse files
.idea/.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
.idea/aws.xml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="accountSettings">
4
+ <option name="activeRegion" value="us-east-1" />
5
+ <option name="recentlyUsedRegions">
6
+ <list>
7
+ <option value="us-east-1" />
8
+ </list>
9
+ </option>
10
+ </component>
11
+ </project>
.idea/inspectionProfiles/Project_Default.xml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <component name="InspectionProjectProfileManager">
2
+ <profile version="1.0">
3
+ <option name="myName" value="Project Default" />
4
+ <inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
5
+ <option name="ignoredPackages">
6
+ <value>
7
+ <list size="3">
8
+ <item index="0" class="java.lang.String" itemvalue="transformers" />
9
+ <item index="1" class="java.lang.String" itemvalue="huggingface-hub" />
10
+ <item index="2" class="java.lang.String" itemvalue="datasets" />
11
+ </list>
12
+ </value>
13
+ </option>
14
+ </inspection_tool>
15
+ <inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
16
+ <option name="ignoredErrors">
17
+ <list>
18
+ <option value="E265" />
19
+ </list>
20
+ </option>
21
+ </inspection_tool>
22
+ </profile>
23
+ </component>
.idea/inspectionProfiles/profiles_settings.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
.idea/misc.xml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Black">
4
+ <option name="sdkName" value="Python 3.11 (viralplay)" />
5
+ </component>
6
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (viralplay)" project-jdk-type="Python SDK" />
7
+ </project>
.idea/modules.xml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/viralplay.iml" filepath="$PROJECT_DIR$/.idea/viralplay.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
.idea/viralplay.iml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <excludeFolder url="file://$MODULE_DIR$/.venv" />
6
+ </content>
7
+ <orderEntry type="inheritedJdk" />
8
+ <orderEntry type="sourceFolder" forTests="false" />
9
+ </component>
10
+ <component name="PyDocumentationSettings">
11
+ <option name="format" value="PLAIN" />
12
+ <option name="myDocStringFormat" value="Plain" />
13
+ </component>
14
+ </module>
app.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ import spaces
4
+ import gradio as gr
5
+
6
+ from handlers import frame_handler_yolo as fh
7
+ from handlers import video_handler as vh
8
+
9
+ model_path = "yolov8n.pt" # YOLOv8 model path
10
+
11
+
12
+ @spaces.GPU(duration=300)
13
+ def process_video(video_file):
14
+ """
15
+ Processes the uploaded video file by extracting key frames, cropping them, and generating a processed video.
16
+ """
17
+ status_message = "Processing started..."
18
+
19
+ # Define output directories
20
+ output_folder = "output_data"
21
+ all_frames_folder = os.path.join(output_folder, "all_frames")
22
+ key_frames_folder = os.path.join(output_folder, "key_frames")
23
+ nonkey_frames_folder = os.path.join(output_folder, "nonkey_frames")
24
+ cropped_frames_folder = os.path.join(output_folder, "cropped_frames")
25
+ processed_video_path = os.path.join(output_folder, "processed_video.mp4")
26
+
27
+ print("Calling process_video function: Output folder:", output_folder)
28
+
29
+ # Clear output directory before processing
30
+ if os.path.exists(output_folder):
31
+ shutil.rmtree(output_folder)
32
+ os.makedirs(output_folder, exist_ok=True)
33
+
34
+ # Save uploaded video temporarily
35
+ video_path = os.path.join(output_folder, "input_video.mp4")
36
+ with open(video_file.name, "rb") as vf:
37
+ with open(video_path, "wb") as f:
38
+ f.write(vf.read())
39
+
40
+ status_message = "Extracting frames..."
41
+ yield status_message, None
42
+
43
+ # Step 1: Extract all frames
44
+ vh.extract_all_frames(video_path, all_frames_folder)
45
+
46
+ status_message = "Detecting key frames..."
47
+ yield status_message, None
48
+
49
+ # Step 2: Extract key frames
50
+ original_fps = 30
51
+ fh.extract_key_frames(all_frames_folder, key_frames_folder, original_fps, model_path)
52
+
53
+ status_message = "Cropping key frames..."
54
+ yield status_message, None
55
+
56
+ # Step 3: Crop key frames based on object detection
57
+ target_resolution = (360, 640) # Output resolution (9:16)
58
+ fh.crop_preserve_key_objects(key_frames_folder, cropped_frames_folder, model_path, target_resolution)
59
+
60
+ status_message = "Generating final video..."
61
+ yield status_message, None
62
+
63
+ # Step 4: Generate short video
64
+ target_resolution = (360, 640) # Output resolution (9:16)
65
+ target_frame_rate = 30
66
+ vh.create_video_from_frames(cropped_frames_folder, processed_video_path, target_frame_rate, target_resolution)
67
+
68
+ status_message = "Processing complete!"
69
+ yield status_message, processed_video_path
70
+
71
+
72
+ # Gradio Blocks UI
73
+ with gr.Blocks() as demo:
74
+ gr.Markdown("## Generate short video for your football match")
75
+ gr.Markdown("Upload a video file. The app will extract key frames, crop them to fix 9:16 aspect ratio, "
76
+ "and generate a short video.")
77
+
78
+ with gr.Row():
79
+ with gr.Column():
80
+ video_input = gr.File(label="Upload Video", type="filepath", file_types=["video"], file_count="single")
81
+ with gr.Column():
82
+ process_button = gr.Button("Process Video", variant="primary")
83
+ status_output = gr.Textbox(label="Status", interactive=False)
84
+ with gr.Column():
85
+ video_output = gr.Video(label="Processed Video", width=360, height=640)
86
+
87
+ process_button.click(process_video, inputs=video_input, outputs=[status_output, video_output])
88
+
89
+ if __name__ == "__main__":
90
+ demo.launch()
handlers/.DS_Store ADDED
Binary file (8.2 kB). View file
 
handlers/__init__.py ADDED
File without changes
handlers/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (176 Bytes). View file
 
handlers/__pycache__/frame_handler_resnet.cpython-311.pyc ADDED
Binary file (8.39 kB). View file
 
handlers/__pycache__/frame_handler_yolo.cpython-311.pyc ADDED
Binary file (13.6 kB). View file
 
handlers/__pycache__/video_handler.cpython-311.pyc ADDED
Binary file (6.94 kB). View file
 
handlers/app_yolo.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import video_handler as vh
2
+ import frame_handler_yolo as fh
3
+
4
+ model_path = "yolov8n.pt" # YOLOv8 model path
5
+ original_fps = 30 # Original FPS of the input videos
6
+
7
+ video_path = "../input_data/football.mp4" # Replace with your video path
8
+
9
+ output_folder = "../output_data/" # Folder to save extracted frames
10
+ all_frames_folder = output_folder + "all_frames"
11
+
12
+ # ============= Step 1: Extract frames from the input videos ========
13
+
14
+ vh.extract_all_frames(video_path, all_frames_folder)
15
+
16
+ #frame_rate = 2 # Extract 2 frames per second
17
+ #vh.extract_frames_by_rate(video_path, all_frames_folder, frame_rate)
18
+
19
+
20
+ #============== Step 2: Extract key frames from the extracted frames ========
21
+ # key frames = frames contains a ball
22
+ # if a previous frame of a key frame is a non-key frame - major movement detected
23
+ # -> reclassify up to 30 previous frames (~ 1 second) as key frames to add context the major movement
24
+
25
+ key_frames_folder = output_folder + "key_frames" # Save key frames here
26
+ nonkey_frames_folder = output_folder + "nonkey_frames" # Save non-key frames here
27
+
28
+ fh.extract_key_frames(all_frames_folder, key_frames_folder, original_fps, model_path)
29
+
30
+ #============== Step 3: Crop the key frames to align with 9:16 ratio aspect while keeping the key object - football ball
31
+ key_frames_9_16_folder = output_folder + "key_frames_9_16" # Save processed frames here
32
+
33
+ target_resolution = (360, 640) # Output resolution (9:16)
34
+
35
+ fh.crop_preserve_key_objects(key_frames_folder, key_frames_9_16_folder, model_path, target_resolution)
36
+
37
+ #============== Step 4: Create a video from the processed frames ========
38
+
39
+ output_video_path_9_16 = output_folder + "output_video_9_16.mp4" # Output video path
40
+ target_frame_rate = 30 # Frames per second of the output videos
41
+ vh.create_video_from_frames(key_frames_9_16_folder, output_video_path_9_16, target_frame_rate, target_resolution)
handlers/app_yolo_test.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import video_handler as vh
2
+ import frame_handler_yolo as fh
3
+
4
+ model_path = "yolov8n.pt" # YOLOv8 model path
5
+ original_fps = 30 # Original FPS of the input videos
6
+
7
+ video_path = "../input_data/football.mp4" # Replace with your video path
8
+
9
+ output_folder = "../output_data/" # Folder to save extracted frames
10
+ all_frames_folder = output_folder + "all_frames"
11
+
12
+ output_video_path_9_16 = output_folder + "output_video_9_16_test.mp4" # Output video path
13
+ target_frame_rate = 30 # Frames per second of the output videos
14
+ target_resolution = (360, 640) # Output resolution (9:16)
15
+
16
+ # FOR TESTING ONLY
17
+ test_key_frames_folder = output_folder + "test_key_frames"
18
+ test_key_frames_9_16_folder = output_folder + "test_key_frames_9_16"
19
+ #fh.crop_preserve_key_objects(test_key_frames_folder, test_key_frames_9_16_folder, model_path, target_resolution)
20
+ vh.create_video_from_frames(test_key_frames_9_16_folder, output_video_path_9_16, target_frame_rate, target_resolution)
handlers/frame_handler_resnet.py ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import torch
4
+ #from transformers import DetrImageProcessor, DetrForObjectDetection
5
+ from transformers import AutoImageProcessor, AutoModelForObjectDetection
6
+ from PIL import Image
7
+ import numpy as np
8
+
9
+ def crop_preserve_key_objects(input_folder, output_folder, model_name='facebook/detr-resnet-50', target_resolution=(360, 640)):
10
+ """
11
+ Preprocess frames to fit a target aspect ratio, focusing on the densest group of people
12
+ if a football is not detected, and extending the area until it reaches the target resolution.
13
+
14
+ Args:
15
+ input_folder (str): Path to the folder containing key frames.
16
+ output_folder (str): Path to save the processed frames.
17
+ model_name (str): Hugging Face model name for DETR.
18
+ target_resolution (tuple): Desired resolution (width, height), e.g., (1920, 1080).
19
+ """
20
+ print("Preprocessing frames using DETR to fit the target aspect ratio...")
21
+
22
+ # Load the DETR model and processor
23
+ #processor = DetrImageProcessor.from_pretrained(model_name)
24
+ #model = DetrForObjectDetection.from_pretrained(model_name)
25
+
26
+ processor = AutoImageProcessor.from_pretrained(model_name)
27
+ model = AutoModelForObjectDetection.from_pretrained(model_name)
28
+ target_aspect_ratio = target_resolution[0] / target_resolution[1]
29
+
30
+ for frame_name in os.listdir(input_folder):
31
+ frame_path = os.path.join(input_folder, frame_name)
32
+ if not frame_name.lower().endswith(('.jpg', '.png')):
33
+ continue # Skip non-image files
34
+
35
+ # Read the frame
36
+ frame = cv2.imread(frame_path)
37
+ if frame is None:
38
+ print(f"Error reading frame: {frame_path}")
39
+ continue
40
+
41
+ original_height, original_width = frame.shape[:2]
42
+ frame_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
43
+
44
+ # Run inference
45
+ inputs = processor(images=frame_pil, return_tensors="pt")
46
+ outputs = model(**inputs)
47
+
48
+ # Extract bounding boxes and labels
49
+ logits = outputs.logits.softmax(-1)[0]
50
+ boxes = outputs.pred_boxes[0].cpu().detach().numpy()
51
+ labels = logits.argmax(-1).cpu().detach().numpy()
52
+ scores = logits.max(-1).values.cpu().detach().numpy()
53
+
54
+ # Filter boxes with a confidence threshold
55
+ confidence_threshold = 0.1
56
+ filtered_boxes = []
57
+ for i, score in enumerate(scores):
58
+ if score >= confidence_threshold:
59
+ filtered_boxes.append((labels[i], score, boxes[i]))
60
+
61
+ # Separate detections into categories
62
+ ball_detected = False
63
+ people_boxes = []
64
+ for label, score, box in filtered_boxes:
65
+ # Convert box from normalized coordinates to pixel values
66
+ x_min, y_min, x_max, y_max = (
67
+ int(box[0] * original_width),
68
+ int(box[1] * original_height),
69
+ int(box[2] * original_width),
70
+ int(box[3] * original_height),
71
+ )
72
+ if label == 32: # "sports ball" class in COCO
73
+ print("Ball is detected in the frame.")
74
+ x_center = (x_min + x_max) // 2
75
+ y_center = (y_min + y_max) // 2
76
+ ball_detected = True
77
+ break
78
+ elif label == 1: # "person" class in COCO
79
+ print("Person is detected in the frame.")
80
+ people_boxes.append((x_min, y_min, x_max, y_max))
81
+
82
+ # If no ball is detected, focus on the densest group of people
83
+ if not ball_detected and people_boxes:
84
+ # Cluster the people into groups based on proximity
85
+ centers = np.array([(int((x1 + x2) / 2), int((y1 + y2) / 2)) for x1, y1, x2, y2 in people_boxes])
86
+ distances = np.linalg.norm(centers[:, None, :] - centers[None, :, :], axis=2)
87
+
88
+ # Define a distance threshold to group nearby people
89
+ threshold = max(original_width, original_height) * 0.1 # Adjust clustering sensitivity
90
+ clusters = []
91
+ visited = set()
92
+
93
+ for i, center in enumerate(centers):
94
+ if i in visited:
95
+ continue
96
+ cluster = [i]
97
+ visited.add(i)
98
+ for j in range(len(centers)):
99
+ if j not in visited and distances[i, j] < threshold:
100
+ cluster.append(j)
101
+ visited.add(j)
102
+ clusters.append(cluster)
103
+
104
+ # Find the largest cluster and calculate its bounding box
105
+ largest_cluster = max(clusters, key=len)
106
+ x_min = min(people_boxes[i][0] for i in largest_cluster)
107
+ y_min = min(people_boxes[i][1] for i in largest_cluster)
108
+ x_max = max(people_boxes[i][2] for i in largest_cluster)
109
+ y_max = max(people_boxes[i][3] for i in largest_cluster)
110
+
111
+ # Center the crop on the largest cluster
112
+ x_center = (x_min + x_max) // 2
113
+ y_center = (y_min + y_max) // 2
114
+
115
+ # Calculate the cropping region to fit the target resolution
116
+ new_width = int(original_height * target_aspect_ratio)
117
+ new_height = int(original_width / target_aspect_ratio)
118
+
119
+ x_start = max(0, x_center - new_width // 2)
120
+ y_start = max(0, y_center - new_height // 2)
121
+ x_end = min(original_width, x_start + new_width)
122
+ y_end = min(original_height, y_start + new_height)
123
+
124
+ # Adjust the crop if the size is smaller than the target resolution
125
+ if (x_end - x_start) < new_width:
126
+ x_start = max(0, x_end - new_width)
127
+ if (y_end - y_start) < new_height:
128
+ y_start = max(0, y_end - new_height)
129
+
130
+ # Crop and resize the frame
131
+ frame_cropped = frame[int(y_start):int(y_end), int(x_start):int(x_end)]
132
+ frame_resized = cv2.resize(frame_cropped, target_resolution, interpolation=cv2.INTER_LINEAR)
133
+
134
+ # Save the processed frame
135
+ output_path = os.path.join(output_folder, frame_name)
136
+ cv2.imwrite(output_path, frame_resized)
137
+ print(f"Processed frame saved: {output_path}")
138
+
139
+ print("Preprocessing completed.")
140
+
141
+
142
+ #back up
143
+ def backup_yolo_crop_preserve_key_objects(input_folder, output_folder, model_path='yolov8n.pt', target_resolution=(360, 640)):
144
+ """
145
+ Preprocess frames to fit a target aspect ratio, focusing on the densest group of people
146
+ if a football is not detected, and extending the area until it reaches the target resolution.
147
+
148
+ Args:
149
+ input_folder (str): Path to the folder containing key frames.
150
+ output_folder (str): Path to save the processed frames.
151
+ model_path (str): Path to the YOLOv8 model file.
152
+ target_resolution (tuple): Desired resolution (width, height), e.g., (1920, 1080).
153
+ """
154
+ print("Preprocessing frames to fit the target aspect ratio...")
155
+
156
+ model = YOLO(model_path)
157
+ target_aspect_ratio = target_resolution[0] / target_resolution[1]
158
+
159
+ for frame_name in os.listdir(input_folder):
160
+ frame_path = os.path.join(input_folder, frame_name)
161
+ if not frame_name.lower().endswith(('.jpg', '.png')):
162
+ continue # Skip non-image files
163
+
164
+ # Read the frame
165
+ frame = cv2.imread(frame_path)
166
+ if frame is None:
167
+ print(f"Error reading frame: {frame_path}")
168
+ continue
169
+
170
+ original_height, original_width = frame.shape[:2]
171
+
172
+ # Run YOLOv8 inference
173
+ # TTP adjusted conf to 0.3 from 0.5 originally
174
+ results = model.predict(frame, conf=0.3)
175
+
176
+ # Initialize cropping region
177
+ x_center, y_center = original_width // 2, original_height // 2
178
+ ball_detected = False
179
+ people_boxes = []
180
+
181
+ # Process detections to find "sports ball" or "person"
182
+ for result in results[0].boxes:
183
+ label = result.cls
184
+ if model.names[int(label)] == "sports ball":
185
+ # Get the center of the detected football
186
+ x_min, y_min, x_max, y_max = result.xyxy[0].numpy()
187
+ x_center = int((x_min + x_max) / 2)
188
+ y_center = int((y_min + y_max) / 2)
189
+ ball_detected = True
190
+ break
191
+ elif model.names[int(label)] == "person":
192
+ # Collect bounding boxes for people
193
+ x_min, y_min, x_max, y_max = result.xyxy[0].numpy()
194
+ people_boxes.append((x_min, y_min, x_max, y_max))
195
+
196
+ # If no ball is detected, focus on the densest group of people
197
+ if not ball_detected and people_boxes:
198
+ # Cluster the people into groups based on proximity
199
+ centers = np.array([(int((x1 + x2) / 2), int((y1 + y2) / 2)) for x1, y1, x2, y2 in people_boxes])
200
+ distances = np.linalg.norm(centers[:, None, :] - centers[None, :, :], axis=2)
201
+
202
+ # Define a distance threshold to group nearby people - Adjust clustering sensitivity
203
+ threshold = max(original_width, original_height) * 0.2 # TTP adjusted to 0.2
204
+ clusters = []
205
+ visited = set()
206
+
207
+ for i, center in enumerate(centers):
208
+ if i in visited:
209
+ continue
210
+ cluster = [i]
211
+ visited.add(i)
212
+ for j in range(len(centers)):
213
+ if j not in visited and distances[i, j] < threshold:
214
+ cluster.append(j)
215
+ visited.add(j)
216
+ clusters.append(cluster)
217
+
218
+ # Find the largest cluster and calculate its bounding box
219
+ largest_cluster = max(clusters, key=len)
220
+ x_min = min(people_boxes[i][0] for i in largest_cluster)
221
+ y_min = min(people_boxes[i][1] for i in largest_cluster)
222
+ x_max = max(people_boxes[i][2] for i in largest_cluster)
223
+ y_max = max(people_boxes[i][3] for i in largest_cluster)
224
+
225
+ # Center the crop on the largest cluster
226
+ x_center = int((x_min + x_max) / 2)
227
+ y_center = int((y_min + y_max) / 2)
228
+
229
+ # Calculate the cropping region to fit the target resolution
230
+ new_width = int(original_height * target_aspect_ratio)
231
+ new_height = int(original_width / target_aspect_ratio)
232
+
233
+ x_start = max(0, x_center - new_width // 2)
234
+ y_start = max(0, y_center - new_height // 2)
235
+ x_end = min(original_width, x_start + new_width)
236
+ y_end = min(original_height, y_start + new_height)
237
+
238
+ # Adjust the crop if the size is smaller than the target resolution
239
+ if (x_end - x_start) < new_width:
240
+ x_start = max(0, x_end - new_width)
241
+ if (y_end - y_start) < new_height:
242
+ y_start = max(0, y_end - new_height)
243
+
244
+ # Crop and resize the frame
245
+ frame_cropped = frame[int(y_start):int(y_end), int(x_start):int(x_end)]
246
+ frame_resized = cv2.resize(frame_cropped, target_resolution, interpolation=cv2.INTER_LINEAR)
247
+
248
+ # Save the processed frame
249
+ output_path = os.path.join(output_folder, frame_name)
250
+ cv2.imwrite(output_path, frame_resized)
251
+ print(f"Processed frame saved: {output_path}")
252
+
253
+ print("Preprocessing completed.")
handlers/frame_handler_yolo.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import shutil
4
+ from collections import deque
5
+ from ultralytics import YOLO # Assuming YOLOv8 library
6
+ import numpy as np
7
+ import functools
8
+ import time
9
+
10
+
11
+ def timer_decorator(func):
12
+ @functools.wraps(func)
13
+ def wrapper(*args, **kwargs):
14
+ start_time = time.time()
15
+ result = func(*args, **kwargs)
16
+ end_time = time.time()
17
+ execution_time = end_time - start_time
18
+ print(f"{func.__name__} took {execution_time:.2f} seconds to execute")
19
+ return result
20
+
21
+ return wrapper
22
+
23
+
24
+ @timer_decorator
25
+ def extract_key_frames(input_folder, key_frames_folder, original_fps=30, model_path='yolov8n.pt'):
26
+ """
27
+ Detects frames containing a football and separates them into key frames.
28
+ Reduces file I/O by loading frames into memory before processing.
29
+
30
+ Optimizations:
31
+ - Reads all frames into memory once to avoid multiple disk reads.
32
+ - Uses OpenCV to write frames instead of shutil.copy (faster).
33
+
34
+ Args:
35
+ input_folder (str): Path to the folder containing input frames.
36
+ key_frames_folder (str): Path to save frames containing a football.
37
+ original_fps: original frames per second (default is 30)
38
+ model_path (str): Path to the YOLOv8 model file (default is yolov8n.pt).
39
+ """
40
+ counter = 0
41
+ print("Extracting key frames with reduced file I/O...")
42
+
43
+ # Ensure the output directory exists
44
+ os.makedirs(key_frames_folder, exist_ok=True)
45
+
46
+ # Load YOLO model once
47
+ model = YOLO(model_path)
48
+
49
+ # Maintain last 30 non-key frames for reclassification
50
+ previous_nonkey_frames = deque(maxlen=original_fps)
51
+ processed_key_frames = set()
52
+ last_frame_was_key = False
53
+
54
+ # Load frames into memory first (Reduces file I/O), sort frames by file names
55
+ frame_names = sorted(os.listdir(input_folder))
56
+ frames = {}
57
+ for frame_name in frame_names:
58
+ if frame_name.lower().endswith(('.jpg', '.png')):
59
+ frame_path = os.path.join(input_folder, frame_name)
60
+ frames[frame_name] = cv2.imread(frame_path) # Load into RAM
61
+
62
+ for frame_name, frame in frames.items():
63
+ if frame is None:
64
+ continue # Skip invalid frames
65
+
66
+ counter += 1
67
+ if counter % 1000 == 0:
68
+ print(f"Processed {counter} frames.")
69
+ # Run YOLO inference
70
+ results = model.predict(frame, conf=0.3, verbose=False)
71
+
72
+ # Check if a football (sports ball) is detected
73
+ ball_detected = any(model.names[int(box.cls)] == "sports ball" for box in results[0].boxes)
74
+
75
+ if ball_detected:
76
+ # Reclassify up to 30 previous non-key frames
77
+ if not last_frame_was_key:
78
+ for _ in range(min(len(previous_nonkey_frames), 30)):
79
+ nonkey_frame_name, nonkey_frame = previous_nonkey_frames.popleft()
80
+ if nonkey_frame_name not in processed_key_frames:
81
+ cv2.imwrite(os.path.join(key_frames_folder, nonkey_frame_name), nonkey_frame)
82
+ processed_key_frames.add(nonkey_frame_name)
83
+
84
+ previous_nonkey_frames.clear() # Reset after reclassification
85
+
86
+ # Save the current frame as a key frame if not already processed
87
+ if frame_name not in processed_key_frames:
88
+ cv2.imwrite(os.path.join(key_frames_folder, frame_name), frame)
89
+ processed_key_frames.add(frame_name)
90
+ last_frame_was_key = True
91
+ else:
92
+ previous_nonkey_frames.append((frame_name, frame))
93
+ last_frame_was_key = False
94
+
95
+ print("Key frame extraction complete (Optimized for File I/O).")
96
+
97
+ @timer_decorator
98
+ def crop_preserve_key_objects(input_folder, output_folder, model_path='yolov8n.pt', target_resolution=(360, 640)):
99
+ """
100
+ Preprocess frames to fit a target aspect ratio, focusing on key objects like a football or the densest group of people.
101
+
102
+ Reuse the cropping area if conditions are met for smoother transitions, and recalculate if objects are out of the reused cropping area.
103
+
104
+ Args:
105
+ input_folder (str): Path to the folder containing key frames.
106
+ output_folder (str): Path to save the processed frames.
107
+ model_path (str): Path to the YOLOv8 model file.
108
+ target_resolution (tuple): Desired resolution (width, height), e.g., (1920, 1080).
109
+ """
110
+ print("Preprocessing frames to fit the target aspect ratio...")
111
+
112
+ ball_counter = 0
113
+ counter = 0
114
+ x_min, y_min, x_max, y_max = 0, 0, 0, 0
115
+
116
+ model = YOLO(model_path)
117
+ target_aspect_ratio = target_resolution[0] / target_resolution[1]
118
+
119
+ os.makedirs(output_folder, exist_ok=True)
120
+
121
+ # Sort frames by file name
122
+ frame_files = sorted(
123
+ [f for f in os.listdir(input_folder) if f.lower().endswith(('.jpg', '.png'))]
124
+ )
125
+
126
+ last_cropping_area = None # Store the last cropping area
127
+ last_objects_detected = None # Track the type of object detected in the last frame ("ball" or "people")
128
+
129
+ for frame_name in frame_files:
130
+ frame_path = os.path.join(input_folder, frame_name)
131
+ if not frame_name.lower().endswith(('.jpg', '.png')):
132
+ continue # Skip non-image files
133
+
134
+ frame = cv2.imread(frame_path)
135
+ if frame is None:
136
+ print(f"Error reading frame: {frame_path}")
137
+ continue
138
+
139
+ counter += 1
140
+ if counter % 100 == 0:
141
+ status_message = f"Preprocessing frames to fit the target aspect ratio...Processed {counter} frames."
142
+ #yield status_message, None
143
+ original_height, original_width = frame.shape[:2]
144
+
145
+ # YOLO inference
146
+ results = model.predict(frame, conf=0.3, verbose=False)
147
+
148
+ # Initialize variables
149
+ x_center, y_center = original_width // 2, original_height // 2
150
+ ball_detected = False
151
+ people_boxes = []
152
+ ball_box = None # To store ball coordinates for case 1
153
+
154
+ # Process detections
155
+ for result in results[0].boxes:
156
+ label = result.cls
157
+ x_min, y_min, x_max, y_max = result.xyxy[0].numpy()
158
+ if model.names[int(label)] == "sports ball":
159
+ ball_detected = True
160
+ ball_box = (x_min, y_min, x_max, y_max)
161
+ ball_counter += 1
162
+ elif model.names[int(label)] == "person":
163
+ people_boxes.append((x_min, y_min, x_max, y_max))
164
+ #draw red boxes around peoples
165
+ #cv2.rectangle(frame, (int(x_min), int(y_min)), (int(x_max), int(y_max)), (0, 0, 255), 2)
166
+
167
+ # Determine whether to reuse the last cropping area
168
+ reuse_last_area = False
169
+ if last_cropping_area:
170
+ if ball_detected and last_objects_detected == "ball":
171
+ # Check if the ball is within the last cropping area
172
+ x_min, y_min, x_max, y_max = last_cropping_area
173
+ if ball_box and (ball_box[0] >= x_min and ball_box[1] >= y_min and
174
+ ball_box[2] <= x_max and ball_box[3] <= y_max):
175
+ reuse_last_area = True
176
+ elif people_boxes and last_objects_detected == "people":
177
+ #result the last_cropping_area until the ball appears
178
+ reuse_last_area = True
179
+
180
+ if reuse_last_area:
181
+ # Reuse the last cropping area
182
+ x_min, y_min, x_max, y_max = last_cropping_area
183
+ else:
184
+ # Calculate a new cropping area
185
+ if ball_detected:
186
+ # Focus on the ball
187
+ x_min, y_min, x_max, y_max = ball_box
188
+ last_objects_detected = "ball"
189
+ elif people_boxes:
190
+ # Find the densest group of people
191
+ x_min, y_min, x_max, y_max = calculate_largest_group_box(people_boxes, original_width, original_height)
192
+
193
+ #draw blue box around densest people area
194
+ #cv2.rectangle(frame, (int(x_min), int(y_min)), (int(x_max), int(y_max)), (255, 0, 0), 2)
195
+ last_objects_detected = "people"
196
+
197
+ # Default to center cropping if nothing is detected
198
+ # Center the crop on the largest cluster
199
+ x_center = (x_min + x_max) // 2
200
+ y_center = (y_min + y_max) // 2
201
+ new_width = int(original_height * target_aspect_ratio)
202
+ new_height = int(original_width / target_aspect_ratio)
203
+ x_min = max(0, x_center - new_width // 2)
204
+ y_min = max(0, y_center - new_height // 2)
205
+ x_max = min(original_width, x_min + new_width)
206
+ y_max = min(original_height, y_min + new_height)
207
+
208
+ # Adjust the crop if the size is smaller than the target resolution
209
+ if (x_max - x_min) < new_width:
210
+ x_min = max(0, x_max - new_width)
211
+ if (y_max - y_min) < new_height:
212
+ y_min = max(0, y_max - new_height)
213
+
214
+ #Update the last cropping area
215
+ last_cropping_area = (x_min, y_min, x_max, y_max)
216
+
217
+ # Crop and resize the frame
218
+ frame_cropped = frame[int(y_min):int(y_max), int(x_min):int(x_max)]
219
+ frame_resized = cv2.resize(frame_cropped, target_resolution, interpolation=cv2.INTER_CUBIC)
220
+
221
+ # Save the processed frame
222
+ output_path = os.path.join(output_folder, frame_name)
223
+ cv2.imwrite(output_path, frame_resized)
224
+
225
+ #save the debug frame
226
+ #debug_path = os.path.join(output_folder, "debug_" + frame_name)
227
+ #cv2.imwrite(debug_path, frame)
228
+
229
+ print("Completed preprocessing frames to fit the target aspect ratio.")
230
+ print(f"Total frames processed: {len(frame_files)}")
231
+ print(f"Total frames detected with a sport ball: {ball_counter}")
232
+
233
+
234
+ def calculate_largest_group_box(people_boxes, original_width, original_height):
235
+ """
236
+ Calculate the bounding box for the densest group of people.
237
+
238
+ Args:
239
+ people_boxes (list of tuples): List of bounding boxes for detected people.
240
+ Each box is (x_min, y_min, x_max, y_max).
241
+ original_width (int): Width of the original frame.
242
+ original_height (int): Height of the original frame.
243
+
244
+ Returns:
245
+ tuple: Bounding box (x_min, y_min, x_max, y_max) for the densest group of people.
246
+ """
247
+ if not people_boxes:
248
+ return None # Return None if no people boxes are provided
249
+
250
+ # Get the center points of all bounding boxes
251
+ centers = np.array([(int((x1 + x2) / 2), int((y1 + y2) / 2)) for x1, y1, x2, y2 in people_boxes])
252
+
253
+ # Calculate pairwise distances between all centers
254
+ distances = np.linalg.norm(centers[:, None, :] - centers[None, :, :], axis=2)
255
+
256
+ # Define a distance threshold for clustering. Adjust this value if needed
257
+ threshold = max(original_width, original_height) * 0.2 # TTP adjusted to 0.2 to allow bigger distance
258
+
259
+ # Perform clustering using a flood-fill approach
260
+ clusters = []
261
+ visited = set()
262
+
263
+ for i, center in enumerate(centers):
264
+ if i in visited:
265
+ continue
266
+ cluster = [i]
267
+ queue = [i]
268
+ visited.add(i)
269
+
270
+ for j in range(len(centers)):
271
+ if j not in visited and distances[i, j] < threshold:
272
+ cluster.append(j)
273
+ visited.add(j)
274
+ clusters.append(cluster)
275
+
276
+ # Find the largest cluster based on the number of people
277
+ largest_cluster = max(clusters, key=len)
278
+
279
+ # Calculate the bounding box for the largest cluster
280
+ x_min = min(people_boxes[i][0] for i in largest_cluster)
281
+ y_min = min(people_boxes[i][1] for i in largest_cluster)
282
+ x_max = max(people_boxes[i][2] for i in largest_cluster)
283
+ y_max = max(people_boxes[i][3] for i in largest_cluster)
284
+
285
+ # Expand the bounding box slightly to include some context
286
+ #padding_x = int(original_width * 0.05) # 5% padding horizontally
287
+ #padding_y = int(original_height * 0.05) # 5% padding vertically
288
+
289
+ #x_min = max(0, x_min - padding_x)
290
+ #y_min = max(0, y_min - padding_y)
291
+ #x_max = min(original_width, x_max + padding_x)
292
+ #y_max = min(original_height, y_max + padding_y)
293
+
294
+ return x_min, y_min, x_max, y_max
handlers/video_handler.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+
4
+ import functools
5
+ import time
6
+
7
+
8
+ def timer_decorator(func):
9
+ @functools.wraps(func)
10
+ def wrapper(*args, **kwargs):
11
+ start_time = time.time()
12
+ result = func(*args, **kwargs)
13
+ end_time = time.time()
14
+ execution_time = end_time - start_time
15
+ print(f"{func.__name__} took {execution_time:.2f} seconds to execute")
16
+ return result
17
+
18
+ return wrapper
19
+
20
+
21
+ @timer_decorator
22
+ def extract_frames_by_rate(video_path, output_folder, frame_rate=1):
23
+ """
24
+ Extracts frames from a video at a specified frame rate.
25
+
26
+ Args:
27
+ video_path (str): Path to the input video file.
28
+ output_folder (str): Directory to save the extracted frames.
29
+ frame_rate (int): Number of frames to extract per second of the video.
30
+ """
31
+ # Ensure the output directory exists
32
+ if not os.path.exists(output_folder):
33
+ os.makedirs(output_folder)
34
+
35
+ # Load the video
36
+ video = cv2.VideoCapture(video_path)
37
+
38
+ # Check if the video is opened successfully
39
+ if not video.isOpened():
40
+ print(f"Error: Cannot open video file {video_path}")
41
+ return
42
+
43
+ # Get video properties
44
+ fps = int(video.get(cv2.CAP_PROP_FPS)) # Frames per second
45
+ total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) # Total number of frames
46
+ duration = total_frames / fps # Duration in seconds
47
+
48
+ print(f"Video loaded: {video_path}")
49
+ print(f"Total Frames: {total_frames}, FPS: {fps}, Duration: {duration:.2f} seconds")
50
+
51
+ # Calculate frame interval (in terms of frame number)
52
+ frame_interval = fps // frame_rate
53
+
54
+ # Frame counter
55
+ frame_count = 0
56
+ saved_count = 0
57
+
58
+ while True:
59
+ # Read a frame
60
+ ret, frame = video.read()
61
+
62
+ # Break the loop if the video ends
63
+ if not ret:
64
+ break
65
+
66
+ # Save frame if it matches the frame interval
67
+ if frame_count % frame_interval == 0:
68
+ frame_filename = os.path.join(output_folder, f"frame_{saved_count:05d}.jpg")
69
+ cv2.imwrite(frame_filename, frame)
70
+ #print(f"Saved: {frame_filename}")
71
+ saved_count += 1
72
+
73
+ frame_count += 1
74
+
75
+ # Release video resources
76
+ video.release()
77
+ print(f"Extraction complete. Total frames saved: {saved_count}")
78
+
79
+
80
+ @timer_decorator
81
+ def extract_all_frames(video_path, output_folder):
82
+ """
83
+ Extracts all frames from a video.
84
+
85
+ Args:
86
+ video_path (str): Path to the input video file.
87
+ output_folder (str): Directory to save the extracted frames.
88
+ """
89
+ # Ensure the output directory exists
90
+ if not os.path.exists(output_folder):
91
+ os.makedirs(output_folder)
92
+
93
+ # Load the video
94
+ video = cv2.VideoCapture(video_path)
95
+
96
+ # Check if the video is opened successfully
97
+ if not video.isOpened():
98
+ print(f"Error: Cannot open video file {video_path}")
99
+ return
100
+
101
+ # Get video properties
102
+ fps = int(video.get(cv2.CAP_PROP_FPS)) # Frames per second
103
+ total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) # Total number of frames
104
+ duration = total_frames / fps # Duration in seconds
105
+
106
+ print(f"Video loaded: {video_path}")
107
+ print(f"Total Frames: {total_frames}, FPS: {fps}, Duration: {duration:.2f} seconds")
108
+ print("Extracting frames from the video...")
109
+ # Frame counter
110
+ frame_count = 0
111
+
112
+ while True:
113
+ # Read a frame
114
+ ret, frame = video.read()
115
+
116
+ # Break the loop if the video ends
117
+ if not ret:
118
+ break
119
+
120
+ # Save every frame
121
+ frame_filename = os.path.join(output_folder, f"frame_{frame_count:05d}.jpg")
122
+ cv2.imwrite(frame_filename, frame)
123
+ frame_count += 1
124
+
125
+ # Release video resources
126
+ video.release()
127
+ print(f"Extraction complete. Total frames saved: {frame_count}")
128
+
129
+
130
+ @timer_decorator
131
+ def create_video_from_frames(input_folder, output_video_path, frame_rate=30, resolution=(360, 640)):
132
+ """
133
+ Creates a video from preprocessed frames.
134
+
135
+ Args:
136
+ input_folder (str): Path to the folder containing frames.
137
+ output_video_path (str): Path to save the output video.
138
+ frame_rate (int): Frames per second for the output video.
139
+ resolution (tuple): Resolution of the output video (width, height).
140
+ """
141
+ # Get sorted list of image files in the folder
142
+ frame_files = sorted(
143
+ [f for f in os.listdir(input_folder) if f.lower().endswith(('.jpg', '.png'))]
144
+ )
145
+
146
+ # Initialize the video writer
147
+ fourcc = cv2.VideoWriter_fourcc(*'avc1') # H.264 Codec
148
+ video_writer = cv2.VideoWriter(output_video_path, fourcc, frame_rate, resolution)
149
+
150
+ for frame_file in frame_files:
151
+ frame_path = os.path.join(input_folder, frame_file)
152
+ frame = cv2.imread(frame_path)
153
+
154
+ if frame is None:
155
+ print(f"Error reading frame: {frame_path}")
156
+ continue
157
+
158
+ # Get the frame's original resolution
159
+ original_height, original_width = frame.shape[:2]
160
+
161
+ # **Check if resizing is needed**
162
+ if (original_width, original_height) != resolution:
163
+ # Ensure the frame matches the target resolution
164
+ frame = cv2.resize(frame, resolution, interpolation=cv2.INTER_CUBIC)
165
+
166
+ # Write the frame to the video
167
+ video_writer.write(frame)
168
+
169
+ # Release the video writer
170
+ video_writer.release()
171
+ print(f"Video saved to: {output_video_path}")
handlers/yolov8n.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f59b3d833e2ff32e194b5bb8e08d211dc7c5bdf144b90d2c8412c47ccfc83b36
3
+ size 6549796
input_data/.DS_Store ADDED
Binary file (6.15 kB). View file
 
requirements.txt ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py==2.1.0
2
+ aiofiles==23.2.1
3
+ aiohttp==3.8.4
4
+ aiosignal==1.3.1
5
+ altair==5.3.0
6
+ annotated-types==0.6.0
7
+ anyio==4.3.0
8
+ appnope==0.1.4
9
+ argon2-cffi==23.1.0
10
+ argon2-cffi-bindings==21.2.0
11
+ arrow==1.3.0
12
+ asttokens==2.4.1
13
+ async-lru==2.0.4
14
+ async-timeout==4.0.2
15
+ attrs==23.1.0
16
+ babel==2.16.0
17
+ beautifulsoup4==4.12.3
18
+ bleach==6.1.0
19
+ boto3==1.28.73
20
+ botocore==1.31.73
21
+ captum==0.7.0
22
+ certifi==2023.5.7
23
+ cffi==1.17.1
24
+ charset-normalizer==3.1.0
25
+ clarabel==0.9.0
26
+ click==8.1.7
27
+ comm==0.2.2
28
+ contourpy==1.2.1
29
+ cvxpy==1.6.0
30
+ cycler==0.12.1
31
+ debugpy==1.8.5
32
+ decorator==5.1.1
33
+ defusedxml==0.7.1
34
+ dill==0.3.7
35
+ dnspython==2.6.1
36
+ email_validator==2.1.1
37
+ executing==2.1.0
38
+ fastapi==0.111.0
39
+ fastapi-cli==0.0.3
40
+ fastjsonschema==2.20.0
41
+ ffmpy==0.3.2
42
+ filelock==3.12.4
43
+ fonttools==4.51.0
44
+ fqdn==1.5.1
45
+ frozenlist==1.3.3
46
+ fsspec==2023.6.0
47
+ gradio_client==0.16.2
48
+ grpcio==1.66.2
49
+ h11==0.14.0
50
+ holidays==0.57
51
+ httpcore==1.0.5
52
+ httptools==0.6.1
53
+ httpx==0.27.0
54
+ huggingface-hub==0.23.0
55
+ idna==3.4
56
+ importlib_resources==6.4.0
57
+ ipykernel==6.29.5
58
+ ipython==8.27.0
59
+ isoduration==20.11.0
60
+ jedi==0.19.1
61
+ Jinja2==3.1.2
62
+ jmespath==1.0.1
63
+ joblib==1.4.2
64
+ json5==0.9.25
65
+ jsonpointer==3.0.0
66
+ jsonschema==4.22.0
67
+ jsonschema-specifications==2023.12.1
68
+ jupyter-events==0.10.0
69
+ jupyter-lsp==2.2.5
70
+ jupyter_client==8.6.2
71
+ jupyter_core==5.7.2
72
+ jupyter_server==2.14.2
73
+ jupyter_server_terminals==0.5.3
74
+ jupyterlab==4.2.5
75
+ jupyterlab_pygments==0.3.0
76
+ jupyterlab_server==2.27.3
77
+ kaleido==0.2.1
78
+ kiwisolver==1.4.5
79
+ liac-arff==2.5.0
80
+ lightning-utilities==0.11.7
81
+ Markdown==3.7
82
+ markdown-it-py==3.0.0
83
+ MarkupSafe==2.1.3
84
+ matplotlib==3.8.4
85
+ matplotlib-inline==0.1.7
86
+ mdurl==0.1.2
87
+ minio==7.2.8
88
+ mistune==3.0.2
89
+ mpmath==1.3.0
90
+ multidict==6.0.4
91
+ multiprocess==0.70.15
92
+ nbclient==0.10.0
93
+ nbconvert==7.16.4
94
+ nbformat==5.10.4
95
+ nest-asyncio==1.6.0
96
+ networkx==3.1
97
+ neuralprophet==0.9.0
98
+ notebook_shim==0.2.4
99
+ numpy==1.26.1
100
+ openai==0.27.7
101
+ openml==0.14.2
102
+ orjson==3.10.3
103
+ osqp==0.6.7.post3
104
+ overrides==7.7.0
105
+ packaging==23.2
106
+ pandas==2.1.1
107
+ pandocfilters==1.5.1
108
+ parso==0.8.4
109
+ pexpect==4.9.0
110
+ pillow==10.3.0
111
+ platformdirs==4.3.2
112
+ plotly==5.24.1
113
+ prometheus_client==0.20.0
114
+ prompt_toolkit==3.0.47
115
+ protobuf==5.28.2
116
+ psutil==6.0.0
117
+ ptyprocess==0.7.0
118
+ pure_eval==0.2.3
119
+ pyarrow==13.0.0
120
+ pycparser==2.22
121
+ pycryptodome==3.20.0
122
+ pydantic==2.7.1
123
+ pydantic_core==2.18.2
124
+ pydub==0.25.1
125
+ Pygments==2.18.0
126
+ pyparsing==3.1.2
127
+ python-dateutil==2.8.2
128
+ python-dotenv==1.0.1
129
+ python-json-logger==2.0.7
130
+ python-multipart==0.0.9
131
+ pytorch-lightning==2.4.0
132
+ pytz==2023.3.post1
133
+ PyYAML==6.0.1
134
+ pyzmq==26.2.0
135
+ qdldl==0.1.7.post4
136
+ referencing==0.35.1
137
+ regex==2023.10.3
138
+ requests==2.31.0
139
+ rfc3339-validator==0.1.4
140
+ rfc3986-validator==0.1.1
141
+ rich==13.7.1
142
+ rpds-py==0.18.1
143
+ ruff==0.4.3
144
+ s3transfer==0.7.0
145
+ safetensors==0.4.0
146
+ scikit-learn==1.5.2
147
+ scipy==1.14.1
148
+ scs==3.2.7
149
+ semantic-version==2.10.0
150
+ Send2Trash==1.8.3
151
+ shellingham==1.5.4
152
+ six==1.16.0
153
+ sniffio==1.3.1
154
+ soupsieve==2.6
155
+ stack-data==0.6.3
156
+ starlette==0.37.2
157
+ sympy==1.12
158
+ tenacity==9.0.0
159
+ tensorboard==2.18.0
160
+ tensorboard-data-server==0.7.2
161
+ terminado==0.18.1
162
+ threadpoolctl==3.5.0
163
+ tinycss2==1.3.0
164
+ tokenizers==0.14.1
165
+ tomlkit==0.12.0
166
+ toolz==0.12.1
167
+ torch==2.1.0
168
+ torchdata==0.7.0
169
+ torchmetrics==1.4.2
170
+ tornado==6.4.1
171
+ tqdm==4.65.0
172
+ traitlets==5.14.3
173
+ typer==0.12.3
174
+ types-python-dateutil==2.9.0.20240906
175
+ typing_extensions==4.8.0
176
+ tzdata==2023.3
177
+ ujson==5.9.0
178
+ uri-template==1.3.0
179
+ urllib3==2.0.2
180
+ uvicorn==0.29.0
181
+ uvloop==0.19.0
182
+ watchfiles==0.21.0
183
+ wcwidth==0.2.13
184
+ webcolors==24.8.0
185
+ webencodings==0.5.1
186
+ websocket-client==1.8.0
187
+ websockets==11.0.3
188
+ Werkzeug==3.0.4
189
+ xmltodict==0.13.0
190
+ xxhash==3.4.1
191
+ yarl==1.9.2
yolov8n.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f59b3d833e2ff32e194b5bb8e08d211dc7c5bdf144b90d2c8412c47ccfc83b36
3
+ size 6549796