Spaces:
Sleeping
Sleeping
File size: 6,025 Bytes
8a8f3ed |
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 |
import os
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
class ImageCaptionOverlay:
"""Handles adding captions to images using OpenCV"""
@staticmethod
def add_caption_overlay(image: np.ndarray, caption: str, position: str = "bottom",
font_size: int = 1, thickness: int = 2) -> np.ndarray:
"""Add caption as overlay on the image"""
img_copy = image.copy()
height, width = img_copy.shape[:2]
# Prepare text
font = cv2.FONT_HERSHEY_SIMPLEX
# Calculate text size and position
text_size = cv2.getTextSize(caption, font, font_size, thickness)[0]
# Wrap text if too long
max_width = width - 40
if text_size[0] > max_width:
words = caption.split()
lines = []
current_line = ""
for word in words:
test_line = current_line + " " + word if current_line else word
test_size = cv2.getTextSize(test_line, font, font_size, thickness)[0]
if test_size[0] <= max_width:
current_line = test_line
else:
if current_line:
lines.append(current_line)
current_line = word
if current_line:
lines.append(current_line)
else:
lines = [caption]
# Calculate positions
line_height = cv2.getTextSize("A", font, font_size, thickness)[0][1] + 10
total_height = len(lines) * line_height
if position == "bottom":
start_y = height - total_height - 20
elif position == "top":
start_y = 30
else: # center
start_y = (height - total_height) // 2
# Add background rectangle for better readability
for i, line in enumerate(lines):
text_size = cv2.getTextSize(line, font, font_size, thickness)[0]
text_x = (width - text_size[0]) // 2
text_y = start_y + (i * line_height) + text_size[1]
# Background rectangle
cv2.rectangle(img_copy,
(text_x - 10, text_y - text_size[1] - 5),
(text_x + text_size[0] + 10, text_y + 5),
(0, 0, 0), -1)
# Text
cv2.putText(img_copy, line, (text_x, text_y), font, font_size, (255, 255, 255), thickness)
return img_copy
@staticmethod
def add_caption_background(image: np.ndarray, caption: str,
font_path: str = None,
background_color: tuple = (0, 0, 0),
text_color: tuple = (255, 255, 255),
margin: int = 50) -> np.ndarray:
"""Add caption on a background behind the image"""
height, width = image.shape[:2]
# Use PIL for better text rendering
pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
# Try to use Poppins font first, then fallback to default
try:
# First priority: custom font path if provided
if font_path and os.path.exists(font_path):
font = ImageFont.truetype(font_path, 24)
# Second priority: check for Poppins font in fonts directory
elif os.path.exists("fonts/Poppins-Regular.ttf"):
font = ImageFont.truetype("fonts/Poppins-Regular.ttf", 24)
else:
# Fallback to default font
font = ImageFont.load_default()
except Exception:
# If anything fails, use default font
font = ImageFont.load_default()
# Calculate text dimensions
draw = ImageDraw.Draw(pil_image)
bbox = draw.textbbox((0, 0), caption, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
# Wrap text if necessary
max_width = width - (2 * margin)
if text_width > max_width:
words = caption.split()
lines = []
current_line = ""
for word in words:
test_line = current_line + " " + word if current_line else word
test_bbox = draw.textbbox((0, 0), test_line, font=font)
test_width = test_bbox[2] - test_bbox[0]
if test_width <= max_width:
current_line = test_line
else:
if current_line:
lines.append(current_line)
current_line = word
if current_line:
lines.append(current_line)
else:
lines = [caption]
# Calculate total text height
total_text_height = len(lines) * text_height + (len(lines) - 1) * 10
# Create new image with space for text
new_height = height + total_text_height + (2 * margin)
new_image = Image.new('RGB', (width, new_height), background_color)
# Paste original image
new_image.paste(pil_image, (0, total_text_height + (2 * margin)))
# Add text
draw = ImageDraw.Draw(new_image)
y_offset = margin
for line in lines:
bbox = draw.textbbox((0, 0), line, font=font)
line_width = bbox[2] - bbox[0]
x_position = (width - line_width) // 2
draw.text((x_position, y_offset), line, fill=text_color, font=font)
y_offset += text_height + 10
# Convert back to OpenCV format
return cv2.cvtColor(np.array(new_image), cv2.COLOR_RGB2BGR) |