Spaces:
Sleeping
Sleeping
File size: 9,384 Bytes
a54527e 324f4d3 a54527e 11a8362 a54527e |
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 |
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
def extract_frame(video_path: str, timestamp: str, output_image_path: str) -> None:
"""
Extracts a frame from a video at the specified timestamp and saves it as an image.
Parameters:
- video_path (str): Path to the input video file.
- timestamp (str): Timestamp in the format "HH:MM:SS.sss".
- output_image_path (str): Path to save the extracted frame image.
"""
# Convert timestamp to milliseconds
h, m, s = map(float, timestamp.split(':'))
target_msec = int((h * 3600 + m * 60 + s) * 1000)
# Open the video
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise IOError(f"Cannot open video file: {video_path}")
# Set position in milliseconds
cap.set(cv2.CAP_PROP_POS_MSEC, target_msec)
# Read the frame
ret, frame = cap.read()
if not ret:
raise RuntimeError(f"Failed to retrieve frame at {timestamp} ({target_msec} ms)")
# Save frame as PNG
cv2.imwrite(output_image_path, frame)
print(f"Frame saved as {output_image_path}")
# Clean up
cap.release()
def center_bbox_in_circle(
input_image_path: str,
bbox: tuple,
output_path: str,
background_path = "src/assets/BACKGROUND.png",
mask_path = "src/assets/circle_mask.png",
circle_center=(296, 126),
circle_radius=77,
blend_alpha: float = 0.3
) -> None:
"""
Centers a bounding box from an input image into a circular area on a background image.
Parameters:
- input_image_path (str): Path to the input image.
- background_path (str): Path to the background image with a circle.
- mask_path (str): Path to the circular mask image (grayscale).
- bbox (tuple): Bounding box in normalized format (y_min, x_min, y_max, x_max), range 0–1000.
- circle_center (tuple): (x, y) center of the target circle on the background.
- circle_radius (int): Radius of the target circle.
- output_path (str): Path to save the resulting image.
- blend_alpha (float): Blending factor for the background (default: 0.3).
"""
background = cv2.imread(background_path)
input_image = cv2.imread(input_image_path)
img_height, img_width = input_image.shape[:2]
y_min = int(bbox[0] / 1000.0 * img_height)
x_min = int(bbox[1] / 1000.0 * img_width)
y_max = int(bbox[2] / 1000.0 * img_height)
x_max = int(bbox[3] / 1000.0 * img_width)
bbox_w = x_max - x_min
bbox_h = y_max - y_min
bbox_center_x = x_min + bbox_w // 2
bbox_center_y = y_min + bbox_h // 2
circle_diameter = 2 * circle_radius
scale = min(circle_diameter / bbox_w, circle_diameter / bbox_h)
new_width = int(img_width * scale)
new_height = int(img_height * scale)
resized_image = cv2.resize(input_image, (new_width, new_height))
new_bbox_center_x = int(bbox_center_x * scale)
new_bbox_center_y = int(bbox_center_y * scale)
offset_x = circle_center[0] - new_bbox_center_x
offset_y = circle_center[1] - new_bbox_center_y
canvas = np.zeros_like(background)
start_x = max(offset_x, 0)
start_y = max(offset_y, 0)
end_x = min(start_x + new_width, background.shape[1])
end_y = min(start_y + new_height, background.shape[0])
src_start_x = max(-offset_x, 0)
src_start_y = max(-offset_y, 0)
target_h = min(end_y - start_y, resized_image.shape[0] - src_start_y)
target_w = min(end_x - start_x, resized_image.shape[1] - src_start_x)
canvas[start_y:start_y+target_h, start_x:start_x+target_w] = resized_image[
src_start_y:src_start_y + target_h,
src_start_x:src_start_x + target_w
]
mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
mask = cv2.resize(mask, (background.shape[1], background.shape[0]))
blended = cv2.addWeighted(canvas, blend_alpha, background, 1.0 - blend_alpha, 0)
mask_3c = cv2.merge([mask, mask, mask])
inv_mask = cv2.bitwise_not(mask_3c)
outside = cv2.bitwise_and(blended, mask_3c)
inside = cv2.bitwise_and(canvas, inv_mask)
combined = cv2.add(outside, inside)
cv2.imwrite(output_path, combined)
print(f"Saved result to {output_path}")
def overlay_image_bottom_right(
background_path: str,
overlay_path: str,
output_path: str
) -> None:
"""
Overlays an image (optionally with transparency) onto the bottom-right corner of another image.
Parameters:
- background_path (str): Path to the background image.
- overlay_path (str): Path to the overlay image (can have alpha channel).
- output_path (str): Path to save the combined image.
"""
# Load images (support alpha if available)
background = cv2.imread(background_path, cv2.IMREAD_UNCHANGED)
overlay = cv2.imread(overlay_path, cv2.IMREAD_UNCHANGED)
h_bg, w_bg = background.shape[:2]
h_ov, w_ov = overlay.shape[:2]
x_offset = w_bg - w_ov
y_offset = h_bg - h_ov
if overlay.shape[2] == 4:
# Split channels
overlay_rgb = overlay[:, :, :3]
alpha = overlay[:, :, 3] / 255.0
# Handle 3-channel background (add alpha if missing)
if background.shape[2] == 3:
background = cv2.cvtColor(background, cv2.COLOR_BGR2BGRA)
for c in range(3): # Only blend RGB channels
background[y_offset:y_offset+h_ov, x_offset:x_offset+w_ov, c] = (
alpha * overlay_rgb[:, :, c] +
(1 - alpha) * background[y_offset:y_offset+h_ov, x_offset:x_offset+w_ov, c]
)
else:
# No alpha channel: paste directly
background[y_offset:y_offset+h_ov, x_offset:x_offset+w_ov] = overlay
# Ensure saving as 3-channel PNG
if background.shape[2] == 4:
background = cv2.cvtColor(background, cv2.COLOR_BGRA2BGR)
cv2.imwrite(output_path, background)
print(f"Combined image saved to {output_path}")
def overlay_image_top_left(
background_path: str,
overlay_path: str,
output_path: str,
padding_x: int = 20,
padding_y: int = 20
) -> None:
"""
Overlays an image (optionally with transparency) onto the top-left corner of another image with padding.
Parameters:
- background_path (str): Path to the background image.
- overlay_path (str): Path to the overlay image (can have alpha channel).
- output_path (str): Path to save the resulting image.
- padding_x (int): Horizontal padding from the left.
- padding_y (int): Vertical padding from the top.
"""
# Load images (support alpha if available)
background = cv2.imread(background_path, cv2.IMREAD_UNCHANGED)
overlay = cv2.imread(overlay_path, cv2.IMREAD_UNCHANGED)
h_bg, w_bg = background.shape[:2]
h_ov, w_ov = overlay.shape[:2]
x_offset = padding_x
y_offset = padding_y
# Ensure overlay fits in background with padding
if x_offset + w_ov > w_bg or y_offset + h_ov > h_bg:
raise ValueError("Overlay image does not fit within background with given padding.")
if overlay.shape[2] == 4:
overlay_rgb = overlay[:, :, :3]
alpha = overlay[:, :, 3] / 255.0
# Add alpha channel to background if needed
if background.shape[2] == 3:
background = cv2.cvtColor(background, cv2.COLOR_BGR2BGRA)
for c in range(3): # Blend RGB channels
background[y_offset:y_offset+h_ov, x_offset:x_offset+w_ov, c] = (
alpha * overlay_rgb[:, :, c] +
(1 - alpha) * background[y_offset:y_offset+h_ov, x_offset:x_offset+w_ov, c]
)
else:
# No alpha: paste directly
background[y_offset:y_offset+h_ov, x_offset:x_offset+w_ov] = overlay
# Convert back to BGR before saving (if needed)
if background.shape[2] == 4:
background = cv2.cvtColor(background, cv2.COLOR_BGRA2BGR)
cv2.imwrite(output_path, background)
print(f"Image with overlay saved to {output_path}")
def annotate_image_with_phrase_and_label(image_path, output_path, phrase, label):
font_path = "src/assets/Herokid-BoldUltraCondensed.otf" # Path to your custom font file
img_cv2 = cv2.imread(image_path)
if img_cv2 is None:
raise ValueError("Could not load the image.")
# Convert BGR (OpenCV) to RGB (Pillow)
img_rgb = cv2.cvtColor(img_cv2, cv2.COLOR_BGR2RGB)
img_pil = Image.fromarray(img_rgb)
draw = ImageDraw.Draw(img_pil)
# Load custom font
font_size = 50
font = ImageFont.truetype(font_path, font_size)
# Image dimensions
width, height = img_pil.size
margin = 10
# Phrase (bottom-left in white)
phrase_bbox = draw.textbbox((0, 0), phrase, font=font)
phrase_width = phrase_bbox[2] - phrase_bbox[0]
phrase_height = phrase_bbox[3] - phrase_bbox[1]
phrase_pos = (margin, height - phrase_height - margin)
draw.text(phrase_pos, phrase, font=font, fill="white")
# Label (bottom-right in black)
label_bbox = draw.textbbox((0, 0), label, font=font)
label_width = label_bbox[2] - label_bbox[0]
label_height = label_bbox[3] - label_bbox[1]
label_pos = (width - label_width - margin, height - label_height - margin)
draw.text(label_pos, label, font=font, fill="black")
# Convert back to OpenCV and save
img_output = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
cv2.imwrite(output_path, img_output) |