Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -595,6 +595,11 @@ class ProfessionalCartoonFilmGenerator:
|
|
| 595 |
else:
|
| 596 |
print(f"β Open-Sora failed for scene {scene_num}, trying fallback...")
|
| 597 |
video_path = self._create_professional_static_video(scene, background_images)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 598 |
else:
|
| 599 |
print(f"π¬ Using static video fallback for scene {scene_num}...")
|
| 600 |
# Fallback to enhanced static video
|
|
@@ -721,7 +726,12 @@ class ProfessionalCartoonFilmGenerator:
|
|
| 721 |
return None
|
| 722 |
|
| 723 |
# Advanced animation effects based on scene mood and type
|
|
|
|
|
|
|
| 724 |
for i in range(total_frames):
|
|
|
|
|
|
|
|
|
|
| 725 |
frame = img_array.copy()
|
| 726 |
progress = i / total_frames
|
| 727 |
|
|
@@ -729,6 +739,8 @@ class ProfessionalCartoonFilmGenerator:
|
|
| 729 |
frame = self._apply_cinematic_effects(frame, scene, progress)
|
| 730 |
out.write(frame)
|
| 731 |
|
|
|
|
|
|
|
| 732 |
out.release()
|
| 733 |
|
| 734 |
if os.path.exists(video_path):
|
|
@@ -747,48 +759,128 @@ class ProfessionalCartoonFilmGenerator:
|
|
| 747 |
|
| 748 |
def _apply_cinematic_effects(self, frame, scene, progress):
|
| 749 |
"""Apply professional cinematic effects"""
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 755 |
|
| 756 |
-
if
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
frame = cv2.warpAffine(frame, M, (w, h))
|
| 762 |
-
|
| 763 |
-
elif 'close-up' in shot_type:
|
| 764 |
-
# Gentle zoom in for emotional moments
|
| 765 |
-
scale = 1.0 + progress * 0.08
|
| 766 |
-
center_x, center_y = w // 2, h // 2
|
| 767 |
-
M = cv2.getRotationMatrix2D((center_x, center_y), 0, scale)
|
| 768 |
-
frame = cv2.warpAffine(frame, M, (w, h))
|
| 769 |
-
|
| 770 |
-
elif mood == 'exciting':
|
| 771 |
-
# Dynamic camera movement
|
| 772 |
-
shift_x = int(np.sin(progress * 4 * np.pi) * 8)
|
| 773 |
-
shift_y = int(np.cos(progress * 2 * np.pi) * 4)
|
| 774 |
-
M = np.float32([[1, 0, shift_x], [0, 1, shift_y]])
|
| 775 |
-
frame = cv2.warpAffine(frame, M, (w, h))
|
| 776 |
-
|
| 777 |
-
elif mood == 'peaceful':
|
| 778 |
-
# Gentle floating motion
|
| 779 |
-
shift_y = int(np.sin(progress * 2 * np.pi) * 6)
|
| 780 |
-
M = np.float32([[1, 0, 0], [0, 1, shift_y]])
|
| 781 |
-
frame = cv2.warpAffine(frame, M, (w, h))
|
| 782 |
-
|
| 783 |
-
elif mood == 'mysterious':
|
| 784 |
-
# Subtle rotation and zoom
|
| 785 |
-
angle = np.sin(progress * np.pi) * 2
|
| 786 |
-
scale = 1.0 + np.sin(progress * np.pi) * 0.05
|
| 787 |
-
center_x, center_y = w // 2, h // 2
|
| 788 |
-
M = cv2.getRotationMatrix2D((center_x, center_y), angle, scale)
|
| 789 |
-
frame = cv2.warpAffine(frame, M, (w, h))
|
| 790 |
|
| 791 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 792 |
|
| 793 |
def _create_emergency_fallback_video(self, script_data: Dict) -> str:
|
| 794 |
"""Create a simple emergency fallback video when everything else fails"""
|
|
|
|
| 595 |
else:
|
| 596 |
print(f"β Open-Sora failed for scene {scene_num}, trying fallback...")
|
| 597 |
video_path = self._create_professional_static_video(scene, background_images)
|
| 598 |
+
|
| 599 |
+
# If professional video fails, try simple video
|
| 600 |
+
if not video_path:
|
| 601 |
+
print(f"π Professional video failed, trying simple video for scene {scene_num}...")
|
| 602 |
+
video_path = self._create_simple_static_video(scene, background_images)
|
| 603 |
else:
|
| 604 |
print(f"π¬ Using static video fallback for scene {scene_num}...")
|
| 605 |
# Fallback to enhanced static video
|
|
|
|
| 726 |
return None
|
| 727 |
|
| 728 |
# Advanced animation effects based on scene mood and type
|
| 729 |
+
print(f"π¬ Generating {total_frames} frames...")
|
| 730 |
+
|
| 731 |
for i in range(total_frames):
|
| 732 |
+
if i % 100 == 0: # Progress update every 100 frames
|
| 733 |
+
print(f" Frame {i}/{total_frames} ({i/total_frames*100:.1f}%)")
|
| 734 |
+
|
| 735 |
frame = img_array.copy()
|
| 736 |
progress = i / total_frames
|
| 737 |
|
|
|
|
| 739 |
frame = self._apply_cinematic_effects(frame, scene, progress)
|
| 740 |
out.write(frame)
|
| 741 |
|
| 742 |
+
print(f"β
All {total_frames} frames generated")
|
| 743 |
+
|
| 744 |
out.release()
|
| 745 |
|
| 746 |
if os.path.exists(video_path):
|
|
|
|
| 759 |
|
| 760 |
def _apply_cinematic_effects(self, frame, scene, progress):
|
| 761 |
"""Apply professional cinematic effects"""
|
| 762 |
+
try:
|
| 763 |
+
h, w = frame.shape[:2]
|
| 764 |
+
|
| 765 |
+
# Choose effect based on scene mood and type
|
| 766 |
+
mood = scene.get('mood', 'heartwarming')
|
| 767 |
+
shot_type = scene.get('shot_type', 'medium shot')
|
| 768 |
+
|
| 769 |
+
if 'establishing' in shot_type:
|
| 770 |
+
# Slow zoom out for establishing shots
|
| 771 |
+
scale = 1.15 - progress * 0.1
|
| 772 |
+
center_x, center_y = w // 2, h // 2
|
| 773 |
+
M = cv2.getRotationMatrix2D((center_x, center_y), 0, scale)
|
| 774 |
+
frame = cv2.warpAffine(frame, M, (w, h))
|
| 775 |
+
|
| 776 |
+
elif 'close-up' in shot_type:
|
| 777 |
+
# Gentle zoom in for emotional moments
|
| 778 |
+
scale = 1.0 + progress * 0.08
|
| 779 |
+
center_x, center_y = w // 2, h // 2
|
| 780 |
+
M = cv2.getRotationMatrix2D((center_x, center_y), 0, scale)
|
| 781 |
+
frame = cv2.warpAffine(frame, M, (w, h))
|
| 782 |
+
|
| 783 |
+
elif mood == 'exciting':
|
| 784 |
+
# Dynamic camera movement
|
| 785 |
+
shift_x = int(np.sin(progress * 4 * np.pi) * 8)
|
| 786 |
+
shift_y = int(np.cos(progress * 2 * np.pi) * 4)
|
| 787 |
+
M = np.float32([[1, 0, shift_x], [0, 1, shift_y]])
|
| 788 |
+
frame = cv2.warpAffine(frame, M, (w, h))
|
| 789 |
+
|
| 790 |
+
elif mood == 'peaceful':
|
| 791 |
+
# Gentle floating motion
|
| 792 |
+
shift_y = int(np.sin(progress * 2 * np.pi) * 6)
|
| 793 |
+
M = np.float32([[1, 0, 0], [0, 1, shift_y]])
|
| 794 |
+
frame = cv2.warpAffine(frame, M, (w, h))
|
| 795 |
+
|
| 796 |
+
elif mood == 'mysterious':
|
| 797 |
+
# Subtle rotation and zoom
|
| 798 |
+
angle = np.sin(progress * np.pi) * 2
|
| 799 |
+
scale = 1.0 + np.sin(progress * np.pi) * 0.05
|
| 800 |
+
center_x, center_y = w // 2, h // 2
|
| 801 |
+
M = cv2.getRotationMatrix2D((center_x, center_y), angle, scale)
|
| 802 |
+
frame = cv2.warpAffine(frame, M, (w, h))
|
| 803 |
+
else:
|
| 804 |
+
# Default: gentle zoom for heartwarming scenes
|
| 805 |
+
scale = 1.0 + progress * 0.03
|
| 806 |
+
center_x, center_y = w // 2, h // 2
|
| 807 |
+
M = cv2.getRotationMatrix2D((center_x, center_y), 0, scale)
|
| 808 |
+
frame = cv2.warpAffine(frame, M, (w, h))
|
| 809 |
+
|
| 810 |
+
return frame
|
| 811 |
+
|
| 812 |
+
except Exception as e:
|
| 813 |
+
print(f"β οΈ Cinematic effect failed: {e}, using original frame")
|
| 814 |
+
return frame
|
| 815 |
+
|
| 816 |
+
def _create_simple_static_video(self, scene: Dict, background_images: Dict) -> str:
|
| 817 |
+
"""Create a simple static video without complex effects"""
|
| 818 |
+
scene_num = scene['scene_number']
|
| 819 |
|
| 820 |
+
if scene_num not in background_images:
|
| 821 |
+
print(f"β No background image for scene {scene_num}")
|
| 822 |
+
return None
|
| 823 |
+
|
| 824 |
+
video_path = f"{self.temp_dir}/simple_scene_{scene_num}.mp4"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 825 |
|
| 826 |
+
try:
|
| 827 |
+
print(f"π¬ Creating simple video for scene {scene_num}...")
|
| 828 |
+
|
| 829 |
+
# Load background image
|
| 830 |
+
bg_path = background_images[scene_num]
|
| 831 |
+
print(f"π Loading background from: {bg_path}")
|
| 832 |
+
|
| 833 |
+
if not os.path.exists(bg_path):
|
| 834 |
+
print(f"β Background file not found: {bg_path}")
|
| 835 |
+
return None
|
| 836 |
+
|
| 837 |
+
image = Image.open(bg_path)
|
| 838 |
+
img_array = np.array(image.resize((1024, 768))) # 4:3 aspect ratio
|
| 839 |
+
img_array = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)
|
| 840 |
+
|
| 841 |
+
print(f"π Image size: {img_array.shape}")
|
| 842 |
+
|
| 843 |
+
# Simple video settings
|
| 844 |
+
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
| 845 |
+
fps = 24
|
| 846 |
+
duration = 10 # Shorter duration for simple video
|
| 847 |
+
total_frames = duration * fps
|
| 848 |
+
|
| 849 |
+
print(f"π¬ Simple video settings: {fps}fps, {duration}s duration, {total_frames} frames")
|
| 850 |
+
|
| 851 |
+
out = cv2.VideoWriter(video_path, fourcc, fps, (1024, 768))
|
| 852 |
+
|
| 853 |
+
if not out.isOpened():
|
| 854 |
+
print(f"β Failed to open simple video writer for {video_path}")
|
| 855 |
+
return None
|
| 856 |
+
|
| 857 |
+
# Simple static video - just repeat the same frame
|
| 858 |
+
print(f"π¬ Generating {total_frames} simple frames...")
|
| 859 |
+
|
| 860 |
+
for i in range(total_frames):
|
| 861 |
+
if i % 50 == 0: # Progress update every 50 frames
|
| 862 |
+
print(f" Frame {i}/{total_frames} ({i/total_frames*100:.1f}%)")
|
| 863 |
+
|
| 864 |
+
# Just use the same frame without effects
|
| 865 |
+
out.write(img_array)
|
| 866 |
+
|
| 867 |
+
print(f"β
All {total_frames} simple frames generated")
|
| 868 |
+
|
| 869 |
+
out.release()
|
| 870 |
+
|
| 871 |
+
if os.path.exists(video_path):
|
| 872 |
+
file_size = os.path.getsize(video_path)
|
| 873 |
+
print(f"β
Simple video created: {video_path} ({file_size / (1024*1024):.1f} MB)")
|
| 874 |
+
return video_path
|
| 875 |
+
else:
|
| 876 |
+
print(f"β Simple video file not created: {video_path}")
|
| 877 |
+
return None
|
| 878 |
+
|
| 879 |
+
except Exception as e:
|
| 880 |
+
print(f"β Simple video creation failed for scene {scene_num}: {e}")
|
| 881 |
+
import traceback
|
| 882 |
+
traceback.print_exc()
|
| 883 |
+
return None
|
| 884 |
|
| 885 |
def _create_emergency_fallback_video(self, script_data: Dict) -> str:
|
| 886 |
"""Create a simple emergency fallback video when everything else fails"""
|