import numpy as np
from PIL import Image, ImageDraw
import io
# Simular carga de imagen (En tu caso usaría la imagen real)
# Como no puedo editar "in-situ" tu archivo subido, genero el script que haría el trabajo
# basándome en la estructura visual de tu imagen (fondo negro, burbujas blancas).
def animate_hearts(image_path):
# 1. Cargar la imagen original
original_img = Image.open(image_path).convert("RGBA")
width, height = original_img.size
# 2. Definir coordenadas APROXIMADAS de los corazones basándome en la imagen vista
# (Coordenadas relativas x, y, ancho, alto)
# Corazón Izquierdo
h1_rect = (int(width * 0.18), int(height * 0.15), int(width * 0.16), int(height * 0.14))
# Corazón Central
h2_rect = (int(width * 0.50), int(height * 0.08), int(width * 0.16), int(height * 0.14))
# Corazón Derecho
h3_rect = (int(width * 0.68), int(height * 0.15), int(width * 0.16), int(height * 0.14))
hearts_rects = [h1_rect, h2_rect, h3_rect]
# 3. Extraer los "sprites" (los corazones) y limpiar el fondo
background = original_img.copy()
draw = ImageDraw.Draw(background)
heart_sprites = []
for rect in hearts_rects:
x, y, w, h = rect
# Recortar el corazón
sprite = original_img.crop((x, y, x+w, y+h))
heart_sprites.append({'img': sprite, 'x': x, 'y': y})
# Borrar del fondo (pintar de negro)
# Usamos un rectángulo negro un poco más grande para asegurar que no queden bordes
draw.rectangle((x-2, y-2, x+w+2, y+h+2), fill=(0,0,0,255))
# 4. Crear los frames de la animación
frames = []
n_frames = 20 # Duración del bucle
for i in range(n_frames):
# Crear copia del fondo limpio
frame = background.copy()
# Calcular fases para movimiento ondulante (seno)
# Usamos fases distintas (0, 2, 4) para que no se muevan al unísono
offsets = [
np.sin((i / n_frames) * 2 * np.pi) * 8, # Corazón 1
np.sin((i / n_frames) * 2 * np.pi + 2) * 8, # Corazón 2 (desfasado)
np.sin((i / n_frames) * 2 * np.pi + 4) * 8 # Corazón 3 (desfasado)
]
# Pegar cada corazón en su nueva posición
for idx, sprite_data in enumerate(heart_sprites):
original_x = sprite_data['x']
original_y = sprite_data['y']
offset_y = offsets[idx]
# Pegar respetando transparencia
frame.paste(sprite_data['img'], (original_x, int(original_y + offset_y)), sprite_data['img'])
frames.append(frame)
# 5. Guardar como GIF
output_buffer = io.BytesIO()
frames[0].save(
output_buffer,
format='GIF',
save_all=True,
append_images=frames[1:],
duration=100, # ms por frame
loop=0
)
output_buffer.seek(0)
return output_buffer
# NOTA: Este código es demostrativo de la lógica que usaré internamente.