Last active
April 22, 2025 14:36
-
-
Save me-suzy/a93ba4acddacd9a4cc5227fde220ac1c to your computer and use it in GitHub Desktop.
cod reparat eroare.txt
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 | |
IMAGE_PATH = r"d:\family-hugging.jpg" | |
OUTPUT_PATH = r"d:\family-hugging-animation.mp4" | |
# Durata animației | |
DURATION = 10 # 10 secunde | |
def apply_ken_burns_effect(clip, duration, zoom_factor=1.2): | |
"""Aplică efectul Ken Burns: zoom și panoramare simulată""" | |
print("Aplic efect Ken Burns...") | |
width, height = clip.size | |
original_w, original_h = clip.size | |
def make_frame(t): | |
# Zoom progresiv | |
scale = 1 + (zoom_factor - 1) * (t / duration) | |
new_w, new_h = int(original_w * scale), int(original_h * scale) | |
# Calculăm poziția pentru panoramare (stânga-dreapta) | |
max_shift = (new_w - original_w) / 2 | |
x_shift = max_shift * np.sin((t / duration) * 2 * np.pi) # Mișcare sinusoidală | |
x_center = (new_w - original_w) / 2 - x_shift | |
y_center = (new_h - original_h) / 2 | |
# Redimensionăm și decupăm imaginea pentru a simula zoom și panoramare | |
resized = clip.resize((new_w, new_h)) | |
cropped = resized.crop( | |
x_center=x_center, | |
y_center=y_center, | |
width=original_w, | |
height=original_h | |
) | |
return cropped.get_frame(t) | |
# Creăm un clip nou cu cadrele generate | |
return clip.set_duration(duration).set_make_frame(make_frame) | |
def create_light_flare_effect(width, height, duration): | |
"""Creează un efect de lumină care se mișcă peste imagine""" | |
print("Creez efect de lumină...") | |
def flare_frame(t): | |
frame = np.zeros((height, width, 3), dtype=np.uint8) | |
flare_center_x = int(width * (t / duration)) | |
flare_center_y = height // 2 + int(100 * np.sin((t * 2 * np.pi) / duration)) | |
for y in range(height): | |
for x in range(width): | |
distance = np.sqrt((x - flare_center_x)**2 + (y - flare_center_y)**2) | |
if distance < 150: | |
intensity = 255 * (1 - distance / 150) | |
frame[y, x] = [int(intensity), int(intensity), 100] | |
return frame | |
flare_clip = ImageClip(flare_frame(0), duration=duration) | |
flare_clip = flare_clip.set_make_frame(lambda t: flare_frame(t)) | |
return flare_clip.set_opacity(0.3) | |
def create_particles_effect(width, height, duration, num_particles=50): | |
"""Creează particule animate (scântei sau petale)""" | |
print("Creez efect de particule...") | |
particles = [] | |
for _ in range(num_particles): | |
x = np.random.randint(0, width) | |
y = np.random.randint(0, height) | |
particle_duration = np.random.uniform(2, duration) | |
def make_particle_frame(t): | |
frame = np.zeros((height, width, 3), dtype=np.uint8) | |
if t < particle_duration: | |
x_pos = x + 50 * np.sin((t * 2 * np.pi) / particle_duration) | |
y_pos = y - t * 50 | |
if 0 <= y_pos < height and 0 <= x_pos < width: | |
cv2.circle(frame, (int(x_pos), int(y_pos)), 2, (255, 255, 200), -1) | |
return frame | |
particle = ImageClip(make_particle_frame(0), duration=duration) | |
particle = particle.set_make_frame(lambda t: make_particle_frame(t)) | |
particles.append(particle.set_opacity(0.7)) | |
return particles | |
def create_color_shift_effect(clip, duration): | |
"""Aplică o tranziție subtilă de luminozitate""" | |
print("Aplic efect de tranziție de luminozitate...") | |
def brightness_shift(t): | |
# Ajustăm luminozitatea cu o variație sinusoidală | |
factor = 0.8 + 0.2 * np.sin((t / duration) * 2 * np.pi) # Variază între 0.8 și 1.0 | |
return factor | |
# Aplicăm efectul de luminozitate | |
return clip.fx(vfx.colorx, brightness_shift) | |
def create_color_tone_effect(clip, duration): | |
"""Aplică o tranziție de tonuri de culoare (cald-rece)""" | |
print("Aplic efect de tranziție de tonuri de culoare...") | |
def make_frame(t): | |
frame = clip.get_frame(t) | |
factor = np.sin((t / duration) * 2 * np.pi) | |
# Ajustăm canalele RGB pentru a simula tranziția cald-rece | |
r_factor = 1 + 0.2 * factor # Accentuăm roșul pentru tonuri calde | |
b_factor = 1 - 0.2 * factor # Accentuăm albastrul pentru tonuri reci | |
new_frame = frame.copy() | |
new_frame[:, :, 0] = np.clip(frame[:, :, 0] * r_factor, 0, 255) # Canalul R | |
new_frame[:, :, 2] = np.clip(frame[:, :, 2] * b_factor, 0, 255) # Canalul B | |
return new_frame | |
return clip.set_make_frame(make_frame) | |
def main(): | |
print(f"Încarc imaginea: {IMAGE_PATH}") | |
try: | |
image = Image.open(IMAGE_PATH) | |
width, height = image.size | |
print(f"Dimensiune imagine: {width}x{height}") | |
base_clip = ImageClip(np.array(image), duration=DURATION) | |
print("Imaginea a fost convertită în clip video") | |
ken_burns_clip = apply_ken_burns_effect(base_clip, DURATION) | |
flare_clip = create_light_flare_effect(width, height, DURATION) | |
particle_clips = create_particles_effect(width, height, DURATION) | |
# Aplicăm mai întâi efectul de luminozitate | |
brightness_clip = create_color_shift_effect(ken_burns_clip, DURATION) | |
# Aplicăm apoi efectul de tranziție de tonuri de culoare | |
final_clip = create_color_tone_effect(brightness_clip, DURATION) | |
print("Combin efectele...") | |
all_clips = [final_clip, flare_clip] + particle_clips | |
final_video = CompositeVideoClip(all_clips) | |
print(f"Salvez animația: {OUTPUT_PATH}") | |
final_video.write_videofile( | |
OUTPUT_PATH, | |
fps=24, | |
codec='libx264', | |
audio_codec='aac', | |
bitrate="2000k", | |
ffmpeg_params=["-crf", "23"] | |
) | |
print("\n✅ Animația a fost creată cu succes!") | |
final_video.close() | |
except Exception as e: | |
print(f"❌ Eroare: {str(e)}") | |
if __name__ == "__main__": | |
main() | |
*** Remote Interpreter Reinitialized *** | |
Încarc imaginea: d:\family-hugging.jpg | |
Dimensiune imagine: 1277x822 | |
Imaginea a fost convertită în clip video | |
Aplic efect Ken Burns... | |
Creez efect de lumină... | |
Creez efect de particule... | |
Aplic efect de tranziție de luminozitate... | |
❌ Eroare: unsupported operand type(s) for *: 'function' and 'int' | |
>>> | |
Eroarea | |
❌ Eroare: unsupported operand type(s) for *: 'function' and 'int' | |
vine din faptul că vfx.colorx aşteaptă un factor numeric, nu o funcție. Când îi dai brightness_shift (o funcție), intern încearcă să înmulțească funcția cu un int și pică. | |
Cum o repari | |
În loc să folosești direct clip.fx(vfx.colorx, brightness_shift), trebuie să aplici dinamic luminozitatea cadru cu cadru, cu .fl sau .fl_image. Iată o variantă cu .fl: | |
def create_color_shift_effect(clip, duration): | |
"""Aplică un shift de luminozitate cadru cu cadru.""" | |
def fl_brightness(get_frame, t): | |
frame = get_frame(t) | |
# calculăm factorul la momentul t | |
factor = 0.8 + 0.2 * np.sin((t / duration) * 2 * np.pi) | |
# multiplicăm frame-ul cu factorul | |
return np.clip(frame * factor, 0, 255).astype('uint8') | |
# .fl primește un callable (get_frame, t) -> frame nou | |
return clip.fl(fl_brightness, apply_to=['mask', 'video']) | |
Ce se întâmplă aici | |
.fl(fl_brightness) – primește ca argument o funcție care, pentru fiecare timp t, primește funcția get_frame(t) și returnează imaginea modificată. | |
În interiorul lui fl_brightness calculăm factor (sinusoidal) și aplicăm direct pe matricea de pixeli: | |
new_frame = frame * factor | |
Astfel, codul nu mai încearcă să înmulțească o funcție cu un int, ci efectuează pe rând operația np.ndarray * float. | |
Integrare în main | |
Înlocuiește definiția create_color_shift_effect din script cu cea de mai sus, apoi: | |
# Aplicăm mai întâi efectul de luminozitate | |
brightness_clip = create_color_shift_effect(ken_burns_clip, DURATION) | |
# Aplicăm apoi efectul de tonare | |
final_clip = create_color_tone_effect(brightness_clip, DURATION) | |
Cu această abordare, animația ta nu va mai genera eroarea și vei obține un shift subtil și fluid de luminozitate pe parcursul celor 10 secunde. | |
Văd că ai un script Python care încearcă să creeze o animație din imaginea ta "family-hugging.jpg", dar întâmpini o eroare. Problema este în funcția create_color_shift_effect unde încerci să aplici un efect de luminozitate variabilă. | |
Eroarea "unsupported operand type(s) for *: 'function' and 'int'" apare deoarece funcția brightness_shift este utilizată incorect cu vfx.colorx. În implementarea actuală, MoviePy se așteaptă ca vfx.colorx să primească un factor constant, nu o funcție. | |
Iată cum poți remedia codul: | |
pythondef create_color_shift_effect(clip, duration): | |
"""Aplică o tranziție subtilă de luminozitate""" | |
print("Aplic efect de tranziție de luminozitate...") | |
def make_frame(t): | |
# Obținem cadrul curent | |
frame = clip.get_frame(t) | |
# Calculăm factorul de luminozitate | |
factor = 0.8 + 0.2 * np.sin((t / duration) * 2 * np.pi) # Variază între 0.8 și 1.0 | |
# Aplicăm factorul direct | |
return frame * factor | |
# Returnăm un nou clip cu frame-urile procesate | |
return clip.fl(lambda gf, t: make_frame(t)) | |
Această modificare înlocuiește utilizarea vfx.colorx cu o abordare mai directă, folosind metoda fl (frame_transform) a MoviePy pentru a aplica transformarea la fiecare cadru. | |
Dacă această soluție nu funcționează, poți încerca și această alternativă mai simplă: | |
pythondef create_color_shift_effect(clip, duration): | |
"""Aplică o tranziție subtilă de luminozitate""" | |
print("Aplic efect de tranziție de luminozitate...") | |
def make_frame(t): | |
frame = clip.get_frame(t) | |
factor = 0.8 + 0.2 * np.sin((t / duration) * 2 * np.pi) | |
return np.clip(frame * factor, 0, 255).astype('uint8') | |
return clip.fl_image(lambda img: make_frame(clip.to_seconds(img))) | |
Asigură-te că ai instalate toate dependențele necesare, inclusiv MoviePy într-o versiune recentă: | |
pip install moviepy numpy pillow opencv-python | |
După această modificare, scriptul tău ar trebui să funcționeze corect și să genereze o animație frumoasă cu efecte Ken Burns, particule și tranziții de lumină. | |
-------Codul corect------ | |
#!/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() | |
Iată codul complet rescris, cu problema din funcția `create_color_shift_effect` rezolvată și câteva optimizări: | |
```python | |
#!/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 | |
IMAGE_PATH = r"d:\family-hugging.jpg" | |
OUTPUT_PATH = r"d:\family-hugging-animation.mp4" | |
# Durata animației | |
DURATION = 10 # 10 secunde | |
def apply_ken_burns_effect(clip, duration, zoom_factor=1.2): | |
"""Aplică efectul Ken Burns: zoom și panoramare simulată""" | |
print("Aplic efect Ken Burns...") | |
width, height = clip.size | |
original_w, original_h = clip.size | |
def make_frame(t): | |
# Zoom progresiv | |
scale = 1 + (zoom_factor - 1) * (t / duration) | |
new_w, new_h = int(original_w * scale), int(original_h * scale) | |
# Calculăm poziția pentru panoramare (stânga-dreapta) | |
max_shift = (new_w - original_w) / 2 | |
x_shift = max_shift * np.sin((t / duration) * 2 * np.pi) # Mișcare sinusoidală | |
x_center = (new_w - original_w) / 2 - x_shift | |
y_center = (new_h - original_h) / 2 | |
# Redimensionăm și decupăm imaginea pentru a simula zoom și panoramare | |
resized = clip.resize((new_w, new_h)) | |
cropped = resized.crop( | |
x_center=x_center, | |
y_center=y_center, | |
width=original_w, | |
height=original_h | |
) | |
return cropped.get_frame(t) | |
# Creăm un clip nou cu cadrele generate | |
return clip.set_duration(duration).set_make_frame(make_frame) | |
def create_light_flare_effect(width, height, duration): | |
"""Creează un efect de lumină care se mișcă peste imagine""" | |
print("Creez efect de lumină...") | |
def flare_frame(t): | |
frame = np.zeros((height, width, 3), dtype=np.uint8) | |
flare_center_x = int(width * (t / duration)) | |
flare_center_y = height // 2 + int(100 * np.sin((t * 2 * np.pi) / duration)) | |
for y in range(height): | |
for x in range(width): | |
distance = np.sqrt((x - flare_center_x)**2 + (y - flare_center_y)**2) | |
if distance < 150: | |
intensity = 255 * (1 - distance / 150) | |
frame[y, x] = [int(intensity), int(intensity), 100] | |
return frame | |
flare_clip = ImageClip(flare_frame(0), duration=duration) | |
flare_clip = flare_clip.set_make_frame(lambda t: flare_frame(t)) | |
return flare_clip.set_opacity(0.3) | |
def create_particles_effect(width, height, duration, num_particles=50): | |
"""Creează particule animate (scântei sau petale)""" | |
print("Creez efect de particule...") | |
particles = [] | |
for _ in range(num_particles): | |
x = np.random.randint(0, width) | |
y = np.random.randint(0, height) | |
particle_duration = np.random.uniform(2, duration) | |
speed = np.random.uniform(30, 70) | |
# Captăm valorile în closure | |
x_start, y_start = x, y | |
p_duration, p_speed = particle_duration, speed | |
def make_particle_frame(t, x=x_start, y=y_start, | |
particle_duration=p_duration, speed=p_speed): | |
frame = np.zeros((height, width, 3), dtype=np.uint8) | |
if t < particle_duration: | |
x_pos = x + 50 * np.sin((t * 2 * np.pi) / particle_duration) | |
y_pos = y - t * speed | |
if 0 <= y_pos < height and 0 <= x_pos < width: | |
cv2.circle(frame, (int(x_pos), int(y_pos)), 2, (255, 255, 200), -1) | |
return frame | |
particle = ImageClip(make_particle_frame(0), duration=duration) | |
particle = particle.set_make_frame(lambda t: make_particle_frame(t)) | |
particles.append(particle.set_opacity(0.7)) | |
return particles | |
def create_color_shift_effect(clip, duration): | |
"""Aplică o tranziție subtilă de luminozitate""" | |
print("Aplic efect de tranziție de luminozitate...") | |
def make_frame(t): | |
# Obținem cadrul curent | |
frame = clip.get_frame(t) | |
# Calculăm factorul de luminozitate | |
factor = 0.8 + 0.2 * np.sin((t / duration) * 2 * np.pi) # Variază între 0.8 și 1.0 | |
# Aplicăm factorul direct și asigurăm că valorile rămân între 0-255 | |
return np.clip(frame * factor, 0, 255).astype('uint8') | |
# Creăm un clip nou cu funcția de transformare | |
return clip.fl_image(lambda img: make_frame(clip.to_seconds(img))) | |
def create_color_tone_effect(clip, duration): | |
"""Aplică o tranziție de tonuri de culoare (cald-rece)""" | |
print("Aplic efect de tranziție de tonuri de culoare...") | |
def tone_transform(get_frame, t): | |
frame = get_frame(t) | |
factor = np.sin((t / duration) * 2 * np.pi) | |
# Ajustăm canalele RGB pentru a simula tranziția cald-rece | |
r_factor = 1 + 0.2 * factor # Accentuăm roșul pentru tonuri calde | |
b_factor = 1 - 0.2 * factor # Accentuăm albastrul pentru tonuri reci | |
new_frame = frame.copy() | |
new_frame[:, :, 0] = np.clip(frame[:, :, 0] * r_factor, 0, 255) # Canalul R | |
new_frame[:, :, 2] = np.clip(frame[:, :, 2] * b_factor, 0, 255) # Canalul B | |
return new_frame | |
return clip.fl(tone_transform) | |
def main(): | |
print(f"Încarc imaginea: {IMAGE_PATH}") | |
try: | |
image = Image.open(IMAGE_PATH) | |
width, height = image.size | |
print(f"Dimensiune imagine: {width}x{height}") | |
base_clip = ImageClip(np.array(image), duration=DURATION) | |
print("Imaginea a fost convertită în clip video") | |
# Aplicăm efectul Ken Burns | |
ken_burns_clip = apply_ken_burns_effect(base_clip, DURATION) | |
# Creăm efectul de lumină | |
flare_clip = create_light_flare_effect(width, height, DURATION) | |
# Creăm particulele | |
particle_clips = create_particles_effect(width, height, DURATION) | |
# Aplicăm efectul de luminozitate | |
enhanced_clip = create_color_shift_effect(ken_burns_clip, DURATION) | |
# Aplicăm efectul de tonuri de culoare | |
final_base_clip = create_color_tone_effect(enhanced_clip, DURATION) | |
print("Combin efectele...") | |
all_clips = [final_base_clip, flare_clip] + particle_clips | |
final_video = CompositeVideoClip(all_clips) | |
print(f"Salvez animația: {OUTPUT_PATH}") | |
final_video.write_videofile( | |
OUTPUT_PATH, | |
fps=24, | |
codec='libx264', | |
audio_codec='aac', | |
bitrate="2000k", | |
ffmpeg_params=["-crf", "23"], | |
threads=4 | |
) | |
print("\n✅ Animația a fost creată cu succes!") | |
except Exception as e: | |
print(f"❌ Eroare: {str(e)}") | |
import traceback | |
traceback.print_exc() | |
if __name__ == "__main__": | |
main() | |
``` | |
Principalele modificări: | |
1. Am rezolvat problema din `create_color_shift_effect` prin utilizarea metodei `fl_image` pentru a aplica transformarea de luminozitate corect. | |
2. Am îmbunătățit funcția `create_color_tone_effect` pentru a utiliza metoda `fl` pentru transformarea cadrelor. | |
3. Am adăugat gestionarea mai robustă a erorilor cu afișarea stack trace-ului complet. | |
4. Am optimizat crearea particulelor pentru a evita problemele cu lambda și valorile închise în closure. | |
5. Am adăugat parametrul `threads=4` la scrierea videoclipului pentru a accelera procesul de export. | |
Acest cod ar trebui să funcționeze fără eroarea anterioară și să producă o animație frumoasă cu efectele dorite. | |
*** Remote Interpreter Reinitialized *** | |
Încarc imaginea: d:\family-hugging.jpg | |
Dimensiune imagine: 1277x822 | |
Imaginea a fost convertită în clip video | |
Aplic efect Ken Burns... | |
Creez efect de lumină... | |
Creez efect de particule... | |
Aplic efect de tranziție de luminozitate... | |
❌ Eroare: 'ImageClip' object has no attribute 'to_seconds' | |
Traceback (most recent call last): | |
File "D:\module1.py", line 150, in main | |
enhanced_clip = create_color_shift_effect(ken_burns_clip, DURATION) | |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
File "D:\module1.py", line 110, in create_color_shift_effect | |
return clip.fl_image(lambda img: make_frame(clip.to_seconds(img))) | |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
File "<decorator-gen-72>", line 2, in fl_image | |
File "C:\Users\necul\AppData\Local\Programs\Python\Python312\Lib\site-packages\moviepy\decorators.py", line 14, in outplace | |
f(newclip, *a, **k) | |
File "C:\Users\necul\AppData\Local\Programs\Python\Python312\Lib\site-packages\moviepy\video\VideoClip.py", line 936, in fl_image | |
arr = image_func(self.get_frame(0)) | |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
File "D:\module1.py", line 110, in <lambda> | |
return clip.fl_image(lambda img: make_frame(clip.to_seconds(img))) | |
^^^^^^^^^^^^^^^ | |
AttributeError: 'ImageClip' object has no attribute 'to_seconds' | |
>>> | |
#!/usr/bin/env python3 | |
import numpy as np | |
from moviepy.editor import VideoClip | |
from PIL import Image | |
import cv2 | |
import time | |
# Calea imaginii | |
IMAGE_PATH = r"d:\family-hugging.jpg" | |
OUTPUT_PATH = r"d:\family-hugging-animation.mp4" | |
# Durata totală și pentru fiecare efect | |
TOTAL_DURATION = 20 # secunde totale | |
EFFECT_DURATION = 5 # durata fiecărui efect | |
def create_animation(image_array): | |
"""Creează un videoclip complet cu toate efectele aplicate secvențial""" | |
h, w = image_array.shape[:2] | |
print(f"Dimensiune imagine originală: {w}x{h}") | |
print(f"Shape array imagine: {image_array.shape}") | |
print(f"Tip date imagine: {image_array.dtype}") | |
print(f"Valori min/max imagine: {np.min(image_array)}/{np.max(image_array)}") | |
# Contorizam cadrele procesate pentru fiecare efect | |
effect1_frames = 0 | |
effect2_frames = 0 | |
effect3_frames = 0 | |
effect4_frames = 0 | |
def make_frame(t): | |
nonlocal effect1_frames, effect2_frames, effect3_frames, effect4_frames | |
# Pentru debugging, afișăm timpul curent la fiecare secundă | |
if t % 1 < 0.034: # Aproximativ un cadru la fiecare secundă (pentru 30fps) | |
print(f"Procesez cadru la timpul t={t:.2f}s") | |
# Determinăm ce efect trebuie aplicat în funcție de timestamp | |
if t < EFFECT_DURATION: # 0-5 secunde: zoom in | |
effect1_frames += 1 | |
if effect1_frames % 30 == 0: # La fiecare ~secunda | |
print(f"Efect 1 (zoom): cadrul {effect1_frames}, t={t:.2f}") | |
# Zoom de la 1.0 la 1.3 | |
zoom = 1.0 + (0.3 * t / EFFECT_DURATION) | |
# Redimensionăm și decupăm pentru efect de zoom | |
zoomed_h, zoomed_w = int(h * zoom), int(w * zoom) | |
zoomed = cv2.resize(image_array, (zoomed_w, zoomed_h), interpolation=cv2.INTER_CUBIC) | |
offset_x = int((zoomed_w - w) / 2) | |
offset_y = int((zoomed_h - h) / 2) | |
try: | |
result = zoomed[offset_y:offset_y + h, offset_x:offset_x + w] | |
if result.shape != (h, w, 3): | |
print(f"ATENȚIE: Formă incorectă rezultat efect 1: {result.shape}, ar trebui {(h, w, 3)}") | |
return result | |
except Exception as e: | |
print(f"EROARE în efect 1: {e}") | |
print(f"zoom={zoom}, zoomed_shape={zoomed.shape}, offset=({offset_x}, {offset_y})") | |
return image_array # Returnăm imaginea originală în caz de eroare | |
elif t < 2 * EFFECT_DURATION: # 5-10 secunde: panoramare stânga-dreapta | |
effect2_frames += 1 | |
if effect2_frames % 30 == 0: # La fiecare ~secunda | |
print(f"Efect 2 (panoramare): cadrul {effect2_frames}, t={t:.2f}") | |
# Timpul relativ în cadrul acestui efect | |
rel_t = t - EFFECT_DURATION | |
# Zoom constant pentru panoramare | |
zoom = 1.2 | |
# Redimensionăm imaginea | |
zoomed_h, zoomed_w = int(h * zoom), int(w * zoom) | |
print(f"Efect 2: zoomed_dim={zoomed_w}x{zoomed_h}, rel_t={rel_t:.2f}") | |
try: | |
zoomed = cv2.resize(image_array, (zoomed_w, zoomed_h), interpolation=cv2.INTER_CUBIC) | |
print(f"Efect 2: zoomed.shape={zoomed.shape}") | |
# Calculăm offset-ul pentru panoramare progresivă | |
progress = rel_t / EFFECT_DURATION | |
max_offset_x = zoomed_w - w | |
offset_x = int(max_offset_x * progress) | |
offset_y = int((zoomed_h - h) / 2) | |
# Ne asigurăm că rămânem în limitele imaginii | |
offset_x = max(0, min(offset_x, zoomed_w - w)) | |
offset_y = max(0, min(offset_y, zoomed_h - h)) | |
print(f"Efect 2: progress={progress:.2f}, offset=({offset_x}, {offset_y})") | |
result = zoomed[offset_y:offset_y + h, offset_x:offset_x + w] | |
print(f"Efect 2: result.shape={result.shape}") | |
if result.shape != (h, w, 3): | |
print(f"ATENȚIE: Formă incorectă rezultat efect 2: {result.shape}, ar trebui {(h, w, 3)}") | |
return result | |
except Exception as e: | |
print(f"EROARE în efect 2: {e}") | |
print(f"zoom={zoom}, zoomed_shape={zoomed_w}x{zoomed_h}, offset=({offset_x}, {offset_y})") | |
# Returnăm imaginea originală în caz de eroare | |
return image_array | |
elif t < 3 * EFFECT_DURATION: # 10-15 secunde: focalizare pe o față | |
effect3_frames += 1 | |
if effect3_frames % 30 == 0: # La fiecare ~secunda | |
print(f"Efect 3 (focalizare): cadrul {effect3_frames}, t={t:.2f}") | |
# Timpul relativ în cadrul acestui efect | |
rel_t = t - (2 * EFFECT_DURATION) | |
# Definim poziția de focalizare (pe unul dintre copii) | |
focus_x, focus_y = 0.4, 0.4 # Ajustează coordonatele pentru a focaliza pe un copil | |
focus_pixel_x = int(w * focus_x) | |
focus_pixel_y = int(h * focus_y) | |
# Zoom progresiv | |
zoom_start = 1.1 | |
zoom_end = 1.5 | |
zoom = zoom_start + (zoom_end - zoom_start) * (rel_t / EFFECT_DURATION) | |
try: | |
# Redimensionăm imaginea | |
zoomed_h, zoomed_w = int(h * zoom), int(w * zoom) | |
zoomed = cv2.resize(image_array, (zoomed_w, zoomed_h), interpolation=cv2.INTER_CUBIC) | |
# Calculăm offset-ul pentru a focaliza pe punctul de interes | |
offset_x = int(focus_pixel_x * zoom) - (w // 2) | |
offset_y = int(focus_pixel_y * zoom) - (h // 2) | |
# Ne asigurăm că rămânem în limitele imaginii | |
offset_x = max(0, min(offset_x, zoomed_w - w)) | |
offset_y = max(0, min(offset_y, zoomed_h - h)) | |
result = zoomed[offset_y:offset_y + h, offset_x:offset_x + w] | |
if result.shape != (h, w, 3): | |
print(f"ATENȚIE: Formă incorectă rezultat efect 3: {result.shape}, ar trebui {(h, w, 3)}") | |
return result | |
except Exception as e: | |
print(f"EROARE în efect 3: {e}") | |
return image_array # Returnăm imaginea originală în caz de eroare | |
else: # 15-20 secunde: efect de pulsație și strălucire | |
effect4_frames += 1 | |
if effect4_frames % 30 == 0: # La fiecare ~secunda | |
print(f"Efect 4 (pulsație): cadrul {effect4_frames}, t={t:.2f}") | |
# Timpul relativ în cadrul acestui efect | |
rel_t = t - (3 * EFFECT_DURATION) | |
try: | |
# Aplicăm un efect de pulsație și strălucire | |
frame = image_array.copy() | |
# Convertim la HSV pentru a manipula saturația și luminozitatea | |
hsv = cv2.cvtColor(frame, cv2.COLOR_RGB2HSV).astype(float) | |
# Factori pentru pulsație | |
brightness_factor = 1.0 + 0.2 * np.sin(rel_t * np.pi) | |
saturation_factor = 1.0 + 0.3 * np.sin(rel_t * np.pi * 1.5) | |
# Aplicăm factorii | |
hsv[:, :, 1] = np.clip(hsv[:, :, 1] * saturation_factor, 0, 255) | |
hsv[:, :, 2] = np.clip(hsv[:, :, 2] * brightness_factor, 0, 255) | |
# Convertim înapoi la RGB | |
result = cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2RGB) | |
# Adăugăm un efect de strălucire | |
blur = cv2.GaussianBlur(result, (21, 21), 0) | |
result = cv2.addWeighted(result, 1.0, blur, 0.2 * np.sin(rel_t * np.pi), 0) | |
if result.shape != (h, w, 3): | |
print(f"ATENȚIE: Formă incorectă rezultat efect 4: {result.shape}, ar trebui {(h, w, 3)}") | |
return result | |
except Exception as e: | |
print(f"EROARE în efect 4: {e}") | |
return image_array # Returnăm imaginea originală în caz de eroare | |
# Creăm un singur VideoClip cu toate efectele combinate | |
return VideoClip(make_frame, duration=TOTAL_DURATION) | |
def main(): | |
print(f"Încarc imaginea: {IMAGE_PATH}") | |
start_time = time.time() | |
try: | |
# Deschidem imaginea folosind PIL și o convertim la numpy array | |
pil_img = Image.open(IMAGE_PATH) | |
img_array = np.array(pil_img) | |
print(f"Imagine încărcată: format={pil_img.format}, mode={pil_img.mode}") | |
print(f"Dimensiune imagine originală: {pil_img.width}x{pil_img.height}") | |
# Verificăm și convertim la RGB dacă e necesar | |
if len(img_array.shape) == 3 and img_array.shape[2] == 4: # Imagine cu canal alfa | |
print("Convertim imagine RGBA la RGB") | |
img_array = cv2.cvtColor(img_array, cv2.COLOR_RGBA2RGB) | |
elif len(img_array.shape) == 2: # Imagine grayscale | |
print("Convertim imagine grayscale la RGB") | |
img_array = cv2.cvtColor(img_array, cv2.COLOR_GRAY2RGB) | |
print(f"Array imagine după conversie: shape={img_array.shape}, dtype={img_array.dtype}") | |
# Creăm videoclipul cu toate efectele | |
print("Creez videoclipul cu toate efectele...") | |
final_clip = create_animation(img_array) | |
# Scriem videoclipul în fișier | |
print(f"Salvez animația: {OUTPUT_PATH}") | |
final_clip.write_videofile( | |
OUTPUT_PATH, | |
fps=30, | |
codec='libx264', | |
audio=False, | |
preset='medium', | |
bitrate="5000k" | |
) | |
elapsed_time = time.time() - start_time | |
print(f"\n✅ Animația a fost creată cu succes în {elapsed_time:.2f} secunde!") | |
except Exception as e: | |
print(f"❌ Eroare generală: {str(e)}") | |
import traceback | |
traceback.print_exc() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment