Spaces:
Paused
Paused
| import gradio as gr | |
| import numpy as np | |
| from PIL import Image | |
| import cv2 | |
| from moviepy.editor import VideoFileClip | |
| from share_btn import community_icon_html, loading_icon_html, share_js | |
| import torch | |
| from diffusers import DiffusionPipeline, DPMSolverMultistepScheduler | |
| from diffusers.utils import export_to_video | |
| def convert_mp4_to_frames(video_path, duration=3): | |
| # Read the video file | |
| video = cv2.VideoCapture(video_path) | |
| # Get the frames per second (fps) of the video | |
| fps = video.get(cv2.CAP_PROP_FPS) | |
| # Calculate the number of frames to extract | |
| num_frames = int(fps * duration) | |
| frames = [] | |
| frame_count = 0 | |
| # Iterate through each frame | |
| while True: | |
| # Read a frame | |
| ret, frame = video.read() | |
| # If the frame was not successfully read or we have reached the desired duration, break the loop | |
| if not ret or frame_count == num_frames: | |
| break | |
| # Convert BGR to RGB | |
| frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
| # Append the frame to the list of frames | |
| frames.append(frame) | |
| frame_count += 1 | |
| # Release the video object | |
| video.release() | |
| # Convert the list of frames to a numpy array | |
| frames = np.array(frames) | |
| return frames | |
| def infer(prompt, video_in, denoise_strength): | |
| negative_prompt = "text, watermark, copyright, blurry, nsfw" | |
| video = convert_mp4_to_frames(video_in, duration=3) | |
| video_resized = [Image.fromarray(frame).resize((1024, 576)) for frame in video] | |
| pipe_xl = DiffusionPipeline.from_pretrained("cerspense/zeroscope_v2_XL", torch_dtype=torch.float16, revision="refs/pr/17") | |
| pipe_xl.vae.enable_slicing() | |
| pipe_xl.scheduler = DPMSolverMultistepScheduler.from_config(pipe_xl.scheduler.config) | |
| pipe_xl.enable_model_cpu_offload() | |
| #pipe_xl.to("cuda") | |
| video_frames = pipe_xl(prompt, negative_prompt=negative_prompt, video=video_resized, strength=denoise_strength).frames | |
| del pipe_xl | |
| torch.cuda.empty_cache() | |
| video_path = export_to_video(video_frames, output_video_path="xl_result.mp4") | |
| return "xl_result.mp4", gr.Group.update(visible=True) | |
| css = """ | |
| #col-container {max-width: 510px; margin-left: auto; margin-right: auto;} | |
| a {text-decoration-line: underline; font-weight: 600;} | |
| .animate-spin { | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| from { | |
| transform: rotate(0deg); | |
| } | |
| to { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| #share-btn-container { | |
| display: flex; | |
| padding-left: 0.5rem !important; | |
| padding-right: 0.5rem !important; | |
| background-color: #000000; | |
| justify-content: center; | |
| align-items: center; | |
| border-radius: 9999px !important; | |
| max-width: 13rem; | |
| } | |
| #share-btn-container:hover { | |
| background-color: #060606; | |
| } | |
| #share-btn { | |
| all: initial; | |
| color: #ffffff; | |
| font-weight: 600; | |
| cursor:pointer; | |
| font-family: 'IBM Plex Sans', sans-serif; | |
| margin-left: 0.5rem !important; | |
| padding-top: 0.5rem !important; | |
| padding-bottom: 0.5rem !important; | |
| right:0; | |
| } | |
| #share-btn * { | |
| all: unset; | |
| } | |
| #share-btn-container div:nth-child(-n+2){ | |
| width: auto !important; | |
| min-height: 0px !important; | |
| } | |
| #share-btn-container .wrap { | |
| display: none !important; | |
| } | |
| #share-btn-container.hidden { | |
| display: none!important; | |
| } | |
| img[src*='#center'] { | |
| display: block; | |
| margin: auto; | |
| } | |
| """ | |
| with gr.Blocks(css=css) as demo: | |
| with gr.Column(elem_id="col-container"): | |
| gr.Markdown( | |
| """ | |
| <h1 style="text-align: center;">Zeroscope XL</h1> | |
| <p style="text-align: center;"> | |
| This space is specifically designed for upscaling content made from <br /> | |
| <a href="https://huggingface.co/spaces/fffiloni/zeroscope">the zeroscope_v2_576w space</a> using vid2vid. <br /> | |
| Remember to use the same prompt that was used to generate the original clip.<br /> | |
| For demo purpose, video length is limited to 3 seconds. | |
| </p> | |
| [](https://huggingface.co/spaces/fffiloni/zeroscope-XL?duplicate=true) | |
| """ | |
| ) | |
| video_in = gr.Video(type="numpy", source="upload") | |
| prompt_in = gr.Textbox(label="Prompt", placeholder="This must be the same prompt you used for the original clip :)", elem_id="prompt-in") | |
| denoise_strength = gr.Slider(label="Denoise strength", minimum=0.6, maximum=0.9, step=0.01, value=0.66) | |
| #inference_steps = gr.Slider(label="Inference Steps", minimum=10, maximum=100, step=1, value=40, interactive=False) | |
| submit_btn = gr.Button("Submit") | |
| video_result = gr.Video(label="Video Output", elem_id="video-output") | |
| with gr.Group(elem_id="share-btn-container", visible=False) as share_group: | |
| community_icon = gr.HTML(community_icon_html) | |
| loading_icon = gr.HTML(loading_icon_html) | |
| share_button = gr.Button("Share to community", elem_id="share-btn") | |
| submit_btn.click(fn=infer, | |
| inputs=[prompt_in, video_in, denoise_strength], | |
| outputs=[video_result, share_group]) | |
| share_button.click(None, [], [], _js=share_js) | |
| demo.queue(max_size=12).launch() | |