Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -811,21 +811,28 @@ def get_video_html(video_path, width="100%"):
|
|
811 |
|
812 |
|
813 |
# *********
|
814 |
-
|
815 |
-
|
816 |
-
"""Validate and preprocess image for video generation with detailed logging"""
|
817 |
try:
|
818 |
st.write("Starting image preprocessing...")
|
819 |
|
820 |
-
#
|
821 |
-
if isinstance(
|
822 |
-
|
823 |
-
|
824 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
825 |
else:
|
826 |
-
raise ValueError(f"Unsupported
|
827 |
|
828 |
-
st.write(f"
|
829 |
|
830 |
# Convert to RGB if necessary
|
831 |
if img.mode != 'RGB':
|
@@ -864,118 +871,20 @@ def validate_and_preprocess_image(image_data, target_size=(576, 1024)):
|
|
864 |
final_img.paste(resized_img, (paste_x, paste_y))
|
865 |
|
866 |
st.write(f"Final image size: {final_img.size}")
|
867 |
-
|
868 |
-
# Validate final image
|
869 |
-
if final_img.size != target_size:
|
870 |
-
raise ValueError(f"Final image size {final_img.size} doesn't match target size {target_size}")
|
871 |
-
|
872 |
return final_img
|
873 |
|
874 |
except Exception as e:
|
875 |
-
st.error(f"Error in image preprocessing: {str(e)}")
|
876 |
return None
|
877 |
|
878 |
-
def generate_video_from_image(image_data, seed=None, motion_bucket_id=127, fps_id=6, max_retries=3):
|
879 |
-
"""Generate video from image with improved preprocessing and error handling"""
|
880 |
-
temp_files = []
|
881 |
-
try:
|
882 |
-
# Set up progress tracking
|
883 |
-
progress_bar = st.progress(0)
|
884 |
-
status_text = st.empty()
|
885 |
-
|
886 |
-
# Preprocess image
|
887 |
-
status_text.text("Preprocessing image...")
|
888 |
-
progress_bar.progress(10)
|
889 |
-
|
890 |
-
processed_img = validate_and_preprocess_image(image_data)
|
891 |
-
if processed_img is None:
|
892 |
-
st.error("Image preprocessing failed")
|
893 |
-
return None, None
|
894 |
-
|
895 |
-
# Show preprocessed image
|
896 |
-
st.write("Preprocessed image preview:")
|
897 |
-
st.image(processed_img, caption="Preprocessed image", use_column_width=True)
|
898 |
-
|
899 |
-
# Save processed image
|
900 |
-
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_img:
|
901 |
-
temp_files.append(temp_img.name)
|
902 |
-
processed_img.save(temp_img.name, format='PNG', optimize=True)
|
903 |
-
st.write(f"Saved preprocessed image to: {temp_img.name}")
|
904 |
-
|
905 |
-
# Verify file size
|
906 |
-
file_size = os.path.getsize(temp_img.name)
|
907 |
-
st.write(f"Preprocessed image file size: {file_size/1024:.2f}KB")
|
908 |
-
|
909 |
-
status_text.text("Connecting to video generation service...")
|
910 |
-
progress_bar.progress(30)
|
911 |
-
|
912 |
-
# Initialize client with debug flags
|
913 |
-
client = Client(
|
914 |
-
"awacke1/stable-video-diffusion",
|
915 |
-
hf_token=os.environ.get("HUGGINGFACE_TOKEN"),
|
916 |
-
)
|
917 |
-
|
918 |
-
if seed is None:
|
919 |
-
seed = int(time.time() * 1000) # Use millisecond timestamp as seed
|
920 |
-
|
921 |
-
status_text.text("Starting video generation...")
|
922 |
-
progress_bar.progress(40)
|
923 |
-
|
924 |
-
for attempt in range(max_retries):
|
925 |
-
try:
|
926 |
-
status_text.text(f"Generating video (attempt {attempt + 1}/{max_retries})...")
|
927 |
-
progress_bar.progress(40 + (attempt * 20))
|
928 |
-
|
929 |
-
# Call video generation API
|
930 |
-
result = client.predict(
|
931 |
-
image=temp_img.name,
|
932 |
-
seed=seed,
|
933 |
-
randomize_seed=False, # Set to False for reproducibility
|
934 |
-
motion_bucket_id=motion_bucket_id,
|
935 |
-
fps_id=fps_id,
|
936 |
-
api_name="/video"
|
937 |
-
)
|
938 |
-
|
939 |
-
# Validate result
|
940 |
-
if result and isinstance(result, tuple) and len(result) >= 1:
|
941 |
-
if isinstance(result[0], dict) and 'video' in result[0]:
|
942 |
-
video_path = result[0]['video']
|
943 |
-
if os.path.exists(video_path):
|
944 |
-
status_text.text("Video generated successfully!")
|
945 |
-
progress_bar.progress(100)
|
946 |
-
return video_path, seed
|
947 |
-
|
948 |
-
st.warning(f"Invalid result format on attempt {attempt + 1}: {result}")
|
949 |
-
time.sleep(2 ** attempt) # Exponential backoff
|
950 |
-
|
951 |
-
except Exception as e:
|
952 |
-
st.warning(f"Attempt {attempt + 1} failed: {str(e)}")
|
953 |
-
time.sleep(2 ** attempt)
|
954 |
-
|
955 |
-
raise Exception(f"Failed to generate video after {max_retries} attempts")
|
956 |
-
|
957 |
-
except Exception as e:
|
958 |
-
st.error(f"Error in video generation: {str(e)}")
|
959 |
-
return None, None
|
960 |
-
|
961 |
-
finally:
|
962 |
-
# Cleanup
|
963 |
-
for temp_file in temp_files:
|
964 |
-
try:
|
965 |
-
if os.path.exists(temp_file):
|
966 |
-
os.unlink(temp_file)
|
967 |
-
st.write(f"Cleaned up temporary file: {temp_file}")
|
968 |
-
except Exception as e:
|
969 |
-
st.warning(f"Error cleaning up {temp_file}: {str(e)}")
|
970 |
-
|
971 |
def add_video_generation_ui(container):
|
972 |
-
"""Enhanced video generation UI with
|
973 |
st.markdown("### 🎥 Video Generation")
|
974 |
|
975 |
col1, col2 = st.columns([2, 1])
|
976 |
|
977 |
with col1:
|
978 |
-
|
979 |
"Upload Image for Video Generation 🖼️",
|
980 |
type=['png', 'jpg', 'jpeg'],
|
981 |
help="Upload a clear, well-lit image. Recommended size: 576x1024 pixels."
|
@@ -998,7 +907,6 @@ def add_video_generation_ui(container):
|
|
998 |
help="Higher values create smoother but potentially less stable videos"
|
999 |
)
|
1000 |
|
1001 |
-
# Add advanced options in an expander
|
1002 |
with st.expander("Advanced Options"):
|
1003 |
use_custom_seed = st.checkbox("Use Custom Seed")
|
1004 |
if use_custom_seed:
|
@@ -1006,71 +914,103 @@ def add_video_generation_ui(container):
|
|
1006 |
else:
|
1007 |
seed = None
|
1008 |
|
1009 |
-
if
|
1010 |
try:
|
|
|
|
|
|
|
1011 |
# Preview original image
|
1012 |
preview_col1, preview_col2 = st.columns(2)
|
1013 |
with preview_col1:
|
1014 |
st.write("Original Image:")
|
1015 |
-
|
|
|
1016 |
|
1017 |
# Preview preprocessed image
|
1018 |
with preview_col2:
|
1019 |
-
|
|
|
1020 |
if preprocessed:
|
1021 |
st.write("Preprocessed Image:")
|
1022 |
st.image(preprocessed, caption="Preprocessed", use_column_width=True)
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
with st.spinner("Processing your video... This may take a few minutes 🎬"):
|
1029 |
-
|
1030 |
-
|
1031 |
-
|
1032 |
-
|
1033 |
-
|
1034 |
-
)
|
1035 |
-
|
1036 |
-
if video_path and os.path.exists(video_path):
|
1037 |
-
# Save video locally
|
1038 |
-
video_filename = f"generated_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
|
1039 |
try:
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
- Seed: {used_seed}
|
1046 |
-
- Motion Intensity: {motion_bucket_id}
|
1047 |
-
- FPS: {fps_id}
|
1048 |
-
""")
|
1049 |
|
1050 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1051 |
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
"
|
1060 |
-
|
1061 |
-
|
1062 |
-
|
1063 |
-
|
1064 |
-
|
1065 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1066 |
else:
|
1067 |
-
st.error(
|
|
|
|
|
|
|
1068 |
except Exception as e:
|
1069 |
-
st.error(f"Error
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1074 |
|
1075 |
# ******************************************
|
1076 |
|
|
|
811 |
|
812 |
|
813 |
# *********
|
814 |
+
def validate_and_preprocess_image(file_data, target_size=(576, 1024)):
|
815 |
+
"""Validate and preprocess image for video generation with improved BytesIO handling"""
|
|
|
816 |
try:
|
817 |
st.write("Starting image preprocessing...")
|
818 |
|
819 |
+
# Handle different input types
|
820 |
+
if isinstance(file_data, bytes):
|
821 |
+
st.write("Processing bytes input")
|
822 |
+
img = Image.open(io.BytesIO(file_data))
|
823 |
+
elif hasattr(file_data, 'read'):
|
824 |
+
st.write("Processing file-like object")
|
825 |
+
# Reset file pointer if possible
|
826 |
+
if hasattr(file_data, 'seek'):
|
827 |
+
file_data.seek(0)
|
828 |
+
img = Image.open(file_data)
|
829 |
+
elif isinstance(file_data, Image.Image):
|
830 |
+
st.write("Processing PIL Image input")
|
831 |
+
img = file_data
|
832 |
else:
|
833 |
+
raise ValueError(f"Unsupported input type: {type(file_data)}")
|
834 |
|
835 |
+
st.write(f"Successfully loaded image: {img.format}, size={img.size}, mode={img.mode}")
|
836 |
|
837 |
# Convert to RGB if necessary
|
838 |
if img.mode != 'RGB':
|
|
|
871 |
final_img.paste(resized_img, (paste_x, paste_y))
|
872 |
|
873 |
st.write(f"Final image size: {final_img.size}")
|
|
|
|
|
|
|
|
|
|
|
874 |
return final_img
|
875 |
|
876 |
except Exception as e:
|
877 |
+
st.error(f"Error in image preprocessing: {str(e)}\nType of input: {type(file_data)}")
|
878 |
return None
|
879 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
880 |
def add_video_generation_ui(container):
|
881 |
+
"""Enhanced video generation UI with improved file handling"""
|
882 |
st.markdown("### 🎥 Video Generation")
|
883 |
|
884 |
col1, col2 = st.columns([2, 1])
|
885 |
|
886 |
with col1:
|
887 |
+
uploaded_file = st.file_uploader(
|
888 |
"Upload Image for Video Generation 🖼️",
|
889 |
type=['png', 'jpg', 'jpeg'],
|
890 |
help="Upload a clear, well-lit image. Recommended size: 576x1024 pixels."
|
|
|
907 |
help="Higher values create smoother but potentially less stable videos"
|
908 |
)
|
909 |
|
|
|
910 |
with st.expander("Advanced Options"):
|
911 |
use_custom_seed = st.checkbox("Use Custom Seed")
|
912 |
if use_custom_seed:
|
|
|
914 |
else:
|
915 |
seed = None
|
916 |
|
917 |
+
if uploaded_file is not None:
|
918 |
try:
|
919 |
+
# Read file data
|
920 |
+
file_data = uploaded_file.read()
|
921 |
+
|
922 |
# Preview original image
|
923 |
preview_col1, preview_col2 = st.columns(2)
|
924 |
with preview_col1:
|
925 |
st.write("Original Image:")
|
926 |
+
original_img = Image.open(io.BytesIO(file_data))
|
927 |
+
st.image(original_img, caption="Original", use_column_width=True)
|
928 |
|
929 |
# Preview preprocessed image
|
930 |
with preview_col2:
|
931 |
+
# Create a new BytesIO object with the file data
|
932 |
+
preprocessed = validate_and_preprocess_image(io.BytesIO(file_data))
|
933 |
if preprocessed:
|
934 |
st.write("Preprocessed Image:")
|
935 |
st.image(preprocessed, caption="Preprocessed", use_column_width=True)
|
936 |
+
else:
|
937 |
+
st.error("Failed to preprocess image")
|
938 |
+
return
|
939 |
+
|
940 |
+
if st.button("🎥 Generate Video", help="Start video generation process"):
|
941 |
with st.spinner("Processing your video... This may take a few minutes 🎬"):
|
942 |
+
# Save preprocessed image to temporary file
|
943 |
+
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
|
944 |
+
preprocessed.save(temp_file.name, format='PNG')
|
945 |
+
st.write(f"Saved preprocessed image to temporary file: {temp_file.name}")
|
946 |
+
|
|
|
|
|
|
|
|
|
|
|
947 |
try:
|
948 |
+
# Initialize the Gradio client
|
949 |
+
client = Client(
|
950 |
+
"awacke1/stable-video-diffusion",
|
951 |
+
hf_token=os.environ.get("HUGGINGFACE_TOKEN")
|
952 |
+
)
|
|
|
|
|
|
|
|
|
953 |
|
954 |
+
# Generate video
|
955 |
+
result = client.predict(
|
956 |
+
image=temp_file.name,
|
957 |
+
seed=seed if seed is not None else int(time.time() * 1000),
|
958 |
+
randomize_seed=seed is None,
|
959 |
+
motion_bucket_id=motion_bucket_id,
|
960 |
+
fps_id=fps_id,
|
961 |
+
api_name="/video"
|
962 |
+
)
|
963 |
|
964 |
+
if result and isinstance(result, tuple) and len(result) >= 1:
|
965 |
+
video_path = result[0].get('video') if isinstance(result[0], dict) else None
|
966 |
+
if video_path and os.path.exists(video_path):
|
967 |
+
# Save video locally
|
968 |
+
video_filename = f"generated_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
|
969 |
+
shutil.copy(video_path, video_filename)
|
970 |
+
|
971 |
+
st.success(f"""
|
972 |
+
Video generated successfully! 🎉
|
973 |
+
- Seed: {seed if seed is not None else 'Random'}
|
974 |
+
- Motion Intensity: {motion_bucket_id}
|
975 |
+
- FPS: {fps_id}
|
976 |
+
""")
|
977 |
+
|
978 |
+
st.video(video_filename)
|
979 |
+
|
980 |
+
# Save to Cosmos DB
|
981 |
+
if container:
|
982 |
+
video_record = {
|
983 |
+
"id": generate_unique_id(),
|
984 |
+
"type": "generated_video",
|
985 |
+
"filename": video_filename,
|
986 |
+
"seed": seed if seed is not None else "random",
|
987 |
+
"motion_bucket_id": motion_bucket_id,
|
988 |
+
"fps_id": fps_id,
|
989 |
+
"timestamp": datetime.now().isoformat()
|
990 |
+
}
|
991 |
+
success, message = insert_record(container, video_record)
|
992 |
+
if success:
|
993 |
+
st.success("Video record saved to database!")
|
994 |
+
else:
|
995 |
+
st.error(f"Error saving video record: {message}")
|
996 |
else:
|
997 |
+
st.error("Failed to generate video: Invalid result format")
|
998 |
+
else:
|
999 |
+
st.error("Failed to generate video: No result returned")
|
1000 |
+
|
1001 |
except Exception as e:
|
1002 |
+
st.error(f"Error generating video: {str(e)}")
|
1003 |
+
finally:
|
1004 |
+
# Cleanup temporary file
|
1005 |
+
try:
|
1006 |
+
os.unlink(temp_file.name)
|
1007 |
+
st.write("Cleaned up temporary file")
|
1008 |
+
except Exception as e:
|
1009 |
+
st.warning(f"Error cleaning up temporary file: {str(e)}")
|
1010 |
+
|
1011 |
+
except Exception as e:
|
1012 |
+
st.error(f"Error processing uploaded file: {str(e)}")
|
1013 |
+
|
1014 |
|
1015 |
# ******************************************
|
1016 |
|