phitran commited on
Commit
ae466e9
·
1 Parent(s): 418ed8f

go back to 3 step appoach, increase model confidence, use ffmpeg

Browse files
app.py CHANGED
@@ -11,7 +11,7 @@ from handlers import video_handler as vh
11
  model_path = "yolov8n.pt" # YOLOv8 model path
12
 
13
 
14
- @spaces.GPU(duration=300)
15
  def process_video(video_file):
16
  """
17
  Processes the uploaded video file by extracting key frames, cropping them, and generating a processed video.
@@ -50,17 +50,21 @@ def process_video(video_file):
50
  status_message = "Extracting frames. Please wait...!"
51
  yield status_message, None
52
 
53
- # Step 1: Extract frames with 18 fps to reduce no of processed frames
54
- frame_rate = 18
55
- vh.extract_frames_by_rate(video_path, all_frames_folder, frame_rate)
56
 
57
- status_message = "Cropping key frames. Please wait...!"
 
58
  yield status_message, None
 
 
59
 
 
 
60
  # Ignore step 2 (extract key frame), do Step 3: Crop key frames based on object detection
61
  target_resolution = (360, 640) # Output resolution (9:16)
62
- #fh.crop_preserve_key_objects(key_frames_folder, cropped_frames_folder, model_path, target_resolution)
63
- fh.crop_preserve_key_objects(all_frames_folder, cropped_frames_folder, model_path, target_resolution)
64
 
65
  status_message = "Generating final video. Please wait...!"
66
  yield status_message, None
@@ -72,7 +76,6 @@ def process_video(video_file):
72
  status_message = "Processing complete!"
73
  yield status_message, processed_video_path
74
 
75
-
76
  # Gradio Blocks UI
77
  with gr.Blocks() as demo:
78
  gr.Markdown("## Generate short video for your football match")
 
11
  model_path = "yolov8n.pt" # YOLOv8 model path
12
 
13
 
14
+ @spaces.GPU(duration=400)
15
  def process_video(video_file):
16
  """
17
  Processes the uploaded video file by extracting key frames, cropping them, and generating a processed video.
 
50
  status_message = "Extracting frames. Please wait...!"
51
  yield status_message, None
52
 
53
+ # Step 1: Extract all frames
54
+ vh.extract_frames_by_rate(video_path, all_frames_folder, original_fps)
 
55
 
56
+ #testing step 2 - extract key frames
57
+ status_message = "Extracting key frames. Please wait...!"
58
  yield status_message, None
59
+ fh.extract_key_frames(all_frames_folder, key_frames_folder, original_fps, model_path)
60
+ #testing
61
 
62
+ status_message = "Cropping key frames. Please wait...!"
63
+ yield status_message, None
64
  # Ignore step 2 (extract key frame), do Step 3: Crop key frames based on object detection
65
  target_resolution = (360, 640) # Output resolution (9:16)
66
+ fh.crop_preserve_key_objects(key_frames_folder, cropped_frames_folder, model_path, target_resolution)
67
+ #fh.crop_preserve_key_objects(all_frames_folder, cropped_frames_folder, model_path, target_resolution)
68
 
69
  status_message = "Generating final video. Please wait...!"
70
  yield status_message, None
 
76
  status_message = "Processing complete!"
77
  yield status_message, processed_video_path
78
 
 
79
  # Gradio Blocks UI
80
  with gr.Blocks() as demo:
81
  gr.Markdown("## Generate short video for your football match")
handlers/frame_handler_yolo.py CHANGED
@@ -47,7 +47,7 @@ def extract_key_frames(input_folder, key_frames_folder, original_fps, model_path
47
  # Load YOLO model once
48
  model = YOLO(model_path)
49
 
50
- # Maintain last 30 non-key frames for reclassification
51
  previous_nonkey_frames = deque(maxlen=original_fps)
52
  processed_key_frames = set()
53
  last_frame_was_key = False
@@ -68,16 +68,16 @@ def extract_key_frames(input_folder, key_frames_folder, original_fps, model_path
68
  if counter % 1000 == 0:
69
  print(f"Processed {counter} frames.")
70
  # Run YOLO inference
71
- results = model.predict(frame, conf=0.3, verbose=False)
72
 
73
  # Check if a football (sports ball) is detected
74
  ball_detected = any(model.names[int(box.cls)] == "sports ball" for box in results[0].boxes)
75
 
76
  if ball_detected:
77
  # TTP: to-do crop the frame
78
- # Reclassify up to 30 previous non-key frames
79
  if not last_frame_was_key:
80
- for _ in range(min(len(previous_nonkey_frames), 30)):
81
  nonkey_frame_name, nonkey_frame = previous_nonkey_frames.popleft()
82
  if nonkey_frame_name not in processed_key_frames:
83
  cv2.imwrite(os.path.join(key_frames_folder, nonkey_frame_name), nonkey_frame)
@@ -145,7 +145,7 @@ def crop_preserve_key_objects(input_folder, output_folder, model_path='yolov8n.p
145
  new_height = int(original_width / target_aspect_ratio)
146
 
147
  # YOLO inference
148
- results = model.predict(frame, conf=0.5, verbose=False)
149
 
150
  # Initialize variables
151
  ball_detected = False
 
47
  # Load YOLO model once
48
  model = YOLO(model_path)
49
 
50
+ # Maintain last non-key frames for reclassification, max = original_fps
51
  previous_nonkey_frames = deque(maxlen=original_fps)
52
  processed_key_frames = set()
53
  last_frame_was_key = False
 
68
  if counter % 1000 == 0:
69
  print(f"Processed {counter} frames.")
70
  # Run YOLO inference
71
+ results = model.predict(frame, conf=0.7, verbose=False)
72
 
73
  # Check if a football (sports ball) is detected
74
  ball_detected = any(model.names[int(box.cls)] == "sports ball" for box in results[0].boxes)
75
 
76
  if ball_detected:
77
  # TTP: to-do crop the frame
78
+ # Reclassify up to {original_fps} previous non-key frames
79
  if not last_frame_was_key:
80
+ for _ in range(min(len(previous_nonkey_frames), original_fps)):
81
  nonkey_frame_name, nonkey_frame = previous_nonkey_frames.popleft()
82
  if nonkey_frame_name not in processed_key_frames:
83
  cv2.imwrite(os.path.join(key_frames_folder, nonkey_frame_name), nonkey_frame)
 
145
  new_height = int(original_width / target_aspect_ratio)
146
 
147
  # YOLO inference
148
+ results = model.predict(frame, conf=0.7, verbose=False)
149
 
150
  # Initialize variables
151
  ball_detected = False
handlers/video_handler.py CHANGED
@@ -1,4 +1,6 @@
1
  import os
 
 
2
  import cv2
3
 
4
  import functools
@@ -20,61 +22,71 @@ def timer_decorator(func):
20
 
21
  @timer_decorator
22
  def extract_frames_by_rate(video_path, output_folder, frame_rate):
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}. FPS used to extracted: {frame_rate}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
 
80
  @timer_decorator
 
1
  import os
2
+ import subprocess
3
+
4
  import cv2
5
 
6
  import functools
 
22
 
23
  @timer_decorator
24
  def extract_frames_by_rate(video_path, output_folder, frame_rate):
 
 
 
 
 
 
 
 
 
25
  if not os.path.exists(output_folder):
26
  os.makedirs(output_folder)
27
+ cmd = [
28
+ 'ffmpeg',
29
+ '-i', video_path,
30
+ '-vf', f'fps={frame_rate}',
31
+ os.path.join(output_folder, 'frame_%05d.jpg')
32
+ ]
33
+ subprocess.run(cmd, check=True)
34
+
35
+ # def extract_frames_by_rate(video_path, output_folder, frame_rate):
36
+ # """
37
+ # Extracts frames from a video at a specified frame rate.
38
+ #
39
+ # Args:
40
+ # video_path (str): Path to the input video file.
41
+ # output_folder (str): Directory to save the extracted frames.
42
+ # frame_rate (int): Number of frames to extract per second of the video.
43
+ # """
44
+ # # Ensure the output directory exists
45
+ # if not os.path.exists(output_folder):
46
+ # os.makedirs(output_folder)
47
+ #
48
+ # # Load the video
49
+ # video = cv2.VideoCapture(video_path)
50
+ #
51
+ # # Check if the video is opened successfully
52
+ # if not video.isOpened():
53
+ # print(f"Error: Cannot open video file {video_path}")
54
+ # return
55
+ #
56
+ # # Get video properties
57
+ # fps = int(video.get(cv2.CAP_PROP_FPS)) # Frames per second
58
+ # total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) # Total number of frames
59
+ # duration = total_frames / fps # Duration in seconds
60
+ #
61
+ # print(f"Video loaded: {video_path}")
62
+ # print(f"Total Frames: {total_frames}, FPS: {fps}, Duration: {duration:.2f} seconds")
63
+ #
64
+ # # Calculate frame interval (in terms of frame number)
65
+ # frame_interval = fps // frame_rate
66
+ #
67
+ # # Frame counter
68
+ # frame_count = 0
69
+ # saved_count = 0
70
+ #
71
+ # while True:
72
+ # # Read a frame
73
+ # ret, frame = video.read()
74
+ #
75
+ # # Break the loop if the video ends
76
+ # if not ret:
77
+ # break
78
+ #
79
+ # # Save frame if it matches the frame interval
80
+ # if frame_count % frame_interval == 0:
81
+ # frame_filename = os.path.join(output_folder, f"frame_{saved_count:05d}.jpg")
82
+ # cv2.imwrite(frame_filename, frame)
83
+ # saved_count += 1
84
+ #
85
+ # frame_count += 1
86
+ #
87
+ # # Release video resources
88
+ # video.release()
89
+ # print(f"Extraction complete. Total frames saved: {saved_count}. FPS used to extracted: {frame_rate}")
90
 
91
 
92
  @timer_decorator
requirements.txt CHANGED
@@ -3,3 +3,4 @@ numpy==2.2.3
3
  opencv_python==4.11.0.86
4
  spaces==0.32.0
5
  ultralytics==8.3.64
 
 
3
  opencv_python==4.11.0.86
4
  spaces==0.32.0
5
  ultralytics==8.3.64
6
+ ffmpeg-python==0.2.0