Created
April 22, 2025 05:56
-
-
Save me-suzy/f38b9d4b5ccb4cd176e09590ae09178b to your computer and use it in GitHub Desktop.
efect flaire.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import os | |
import numpy as np | |
from moviepy.editor import ImageClip, CompositeVideoClip, vfx | |
from PIL import Image | |
import cv2 | |
# Calea imaginii și a fișierului de ieșire | |
IMAGE_PATH = r"d:\family-hugging.jpg" | |
OUTPUT_PATH = r"d:\family-hugging-animation.mp4" | |
# Durata animației (secunde) | |
DURATION = 10 | |
def read_file_with_fallback_encoding(file_path): | |
"""Încearcă mai multe encodări pentru a citi un fișier text.""" | |
encodings = ['utf-8', 'latin1', 'cp1252', 'iso-8859-1'] | |
for encoding in encodings: | |
try: | |
with open(file_path, 'r', encoding=encoding): | |
return open(file_path, 'r', encoding=encoding).read() | |
except UnicodeDecodeError: | |
continue | |
print(f"Nu s-a putut citi fișierul {file_path}") | |
return None | |
def replace_special_chars(val): | |
"""Înlocuiește dash-uri tipografice cu minus simplu.""" | |
return val.replace('–', '-').replace('—', '-') | |
def normalize_value(val): | |
"""Normalizează un șir: spații non-break, dash-uri speciale, strip+lower.""" | |
if val is None: | |
return None | |
val = val.replace('\xa0', ' ') | |
val = replace_special_chars(val) | |
return val.strip().lower() | |
def apply_ken_burns_effect(clip, duration, zoom_factor=1.2): | |
"""Aplică efectul Ken Burns: zoom și panoramare sinusoidală.""" | |
original_w, original_h = clip.size | |
def make_frame(t): | |
# calcul zoom | |
scale = 1 + (zoom_factor - 1) * (t / duration) | |
new_w, new_h = int(original_w * scale), int(original_h * scale) | |
# panoramare pe orizontală | |
max_shift = (new_w - original_w) / 2 | |
x_shift = max_shift * np.sin((t / duration) * 2 * np.pi) | |
x_center = (new_w - original_w) / 2 - x_shift | |
y_center = (new_h - original_h) / 2 | |
resized = clip.resize((new_w, new_h)) | |
return resized.crop( | |
x_center=x_center, | |
y_center=y_center, | |
width=original_w, | |
height=original_h | |
).get_frame(t) | |
return clip.set_duration(duration).set_make_frame(make_frame) | |
def create_light_flare_effect(width, height, duration): | |
"""Creează un flare lumină care se mișcă și oscilează pe verticală.""" | |
def flare_frame(t): | |
frame = np.zeros((height, width, 3), dtype=np.uint8) | |
fx = int(width * (t / duration)) | |
fy = height // 2 + int(100 * np.sin((t * 2 * np.pi) / duration)) | |
for y in range(height): | |
for x in range(width): | |
d = np.hypot(x - fx, y - fy) | |
if d < 150: | |
intensity = 255 * (1 - d / 150) | |
frame[y, x] = [int(intensity), int(intensity), 100] | |
return frame | |
clip = ImageClip(flare_frame(0), duration=duration) | |
clip = clip.set_make_frame(lambda t: flare_frame(t)) | |
return clip.set_opacity(0.3) | |
def create_particles_effect(width, height, duration, num_particles=50): | |
"""Creează particule animate (scântei/petale) care urcă și oscilează.""" | |
particles = [] | |
for _ in range(num_particles): | |
x0 = np.random.randint(0, width) | |
y0 = np.random.randint(0, height) | |
pd = np.random.uniform(2, duration) | |
def make_particle_frame(t, x0=x0, y0=y0, pd=pd): | |
frame = np.zeros((height, width, 3), dtype=np.uint8) | |
if t < pd: | |
x = x0 + 50 * np.sin((t * 2 * np.pi) / pd) | |
y = y0 - t * 50 | |
if 0 <= x < width and 0 <= y < height: | |
cv2.circle(frame, (int(x), int(y)), 2, (255, 255, 200), -1) | |
return frame | |
pclip = ImageClip(make_particle_frame(0), duration=duration) | |
pclip = pclip.set_make_frame(lambda t, f=make_particle_frame: f(t)) | |
particles.append(pclip.set_opacity(0.7)) | |
return particles | |
def create_color_shift_effect(clip, duration): | |
"""Aplică shift de luminozitate cadru cu cadru.""" | |
def fl_brightness(get_frame, t): | |
frame = get_frame(t).astype(np.float32) | |
factor = 0.8 + 0.2 * np.sin((t / duration) * 2 * np.pi) | |
shifted = np.clip(frame * factor, 0, 255) | |
return shifted.astype('uint8') | |
return clip.fl(fl_brightness, apply_to=['video', 'mask']) | |
def create_color_tone_effect(clip, duration): | |
"""Aplică tranziție cald–rece pe tonurile de culoare.""" | |
def make_frame(t): | |
frame = clip.get_frame(t).astype(np.float32) | |
f = np.sin((t / duration) * 2 * np.pi) | |
r_factor = 1 + 0.2 * f | |
b_factor = 1 - 0.2 * f | |
frame[:, :, 0] = np.clip(frame[:, :, 0] * r_factor, 0, 255) | |
frame[:, :, 2] = np.clip(frame[:, :, 2] * b_factor, 0, 255) | |
return frame.astype('uint8') | |
return clip.set_make_frame(make_frame) | |
def main(): | |
print(f"Încarc imaginea: {IMAGE_PATH}") | |
image = Image.open(IMAGE_PATH) | |
width, height = image.size | |
print(f"Dimensiune imagine: {width}x{height}") | |
base = ImageClip(np.array(image), duration=DURATION) | |
print("Aplic efect Ken Burns...") | |
kb = apply_ken_burns_effect(base, DURATION) | |
print("Creez efect flare...") | |
flare = create_light_flare_effect(width, height, DURATION) | |
print("Creez particule...") | |
particles = create_particles_effect(width, height, DURATION) | |
print("Aplic efect shift de luminozitate...") | |
bright = create_color_shift_effect(kb, DURATION) | |
print("Aplic efect ton de culoare...") | |
toned = create_color_tone_effect(bright, DURATION) | |
print("Combin toate stratificările...") | |
clips = [toned, flare] + particles | |
final = CompositeVideoClip(clips) | |
print(f"Salvez animația în: {OUTPUT_PATH}") | |
final.write_videofile( | |
OUTPUT_PATH, | |
fps=24, | |
codec='libx264', | |
audio_codec='aac', | |
bitrate="2000k", | |
ffmpeg_params=["-crf", "23"] | |
) | |
final.close() | |
print("✅ Animația a fost creată cu succes!") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment