CoolFace
Apppublic

SubstanceSHIFT/SeedVR2-3B

sourceHugging Faceapache-2.0updated 1y agoView on Hugging Face
0likes
app.py510 linesDownload Raw Back to root
1# // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates2# //3# // Licensed under the Apache License, Version 2.0 (the "License");4# // you may not use this file except in compliance with the License.5# // You may obtain a copy of the License at6# //7# //     http://www.apache.org/licenses/LICENSE-2.08# //9# // Unless required by applicable law or agreed to in writing, software10# // distributed under the License is distributed on an "AS IS" BASIS,11# // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# // See the License for the specific language governing permissions and13# // limitations under the License.14import spaces15import subprocess16 17import os18import torch19import mediapy20from einops import rearrange21from omegaconf import OmegaConf22print(os.getcwd())23import datetime24from tqdm import tqdm25import gc26 27from data.image.transforms.divisible_crop import DivisibleCrop28from data.image.transforms.na_resize import NaResize29from data.video.transforms.rearrange import Rearrange30if os.path.exists("./projects/video_diffusion_sr/color_fix.py"):31    from projects.video_diffusion_sr.color_fix import wavelet_reconstruction32    use_colorfix=True33else:34    use_colorfix = False35    print('Note!!!!!! Color fix is not avaliable!')36from torchvision.transforms import Compose, Lambda, Normalize37from torchvision.io.video import read_video38import argparse39from PIL import Image40 41from common.distributed import (42    get_device,43    init_torch,44)45 46from common.distributed.advanced import (47    get_data_parallel_rank,48    get_data_parallel_world_size,49    get_sequence_parallel_rank,50    get_sequence_parallel_world_size,51    init_sequence_parallel,52)53 54from projects.video_diffusion_sr.infer import VideoDiffusionInfer55from common.config import load_config56from common.distributed.ops import sync_data57from common.seed import set_seed58from common.partition import partition_by_groups, partition_by_size59 60import gradio as gr61from pathlib import Path62from urllib.parse import urlparse63from torch.hub import download_url_to_file, get_dir64import shlex65import uuid66import mimetypes67import torchvision.transforms as T68 69os.environ["MASTER_ADDR"] = "127.0.0.1"70os.environ["MASTER_PORT"] = "12355"71os.environ["RANK"] = str(0)72os.environ["WORLD_SIZE"] = str(1)73 74subprocess.run(75    "pip install flash-attn --no-build-isolation",76    env={"FLASH_ATTENTION_SKIP_CUDA_BUILD": "TRUE"},77    shell=True,78)79 80def load_file_from_url(url, model_dir=None, progress=True, file_name=None):81    """Load file from http url, will download models if necessary.82 83    Reference: https://github.com/1adrianb/face-alignment/blob/master/face_alignment/utils.py84 85    Args:86        url (str): URL to be downloaded.87        model_dir (str): The path to save the downloaded model. Should be a full path. If None, use pytorch hub_dir.88            Default: None.89        progress (bool): Whether to show the download progress. Default: True.90        file_name (str): The downloaded file name. If None, use the file name in the url. Default: None.91 92    Returns:93        str: The path to the downloaded file.94    """95    if model_dir is None:  # use the pytorch hub_dir96        hub_dir = get_dir()97        model_dir = os.path.join(hub_dir, 'checkpoints')98 99    os.makedirs(model_dir, exist_ok=True)100 101    parts = urlparse(url)102    filename = os.path.basename(parts.path)103    if file_name is not None:104        filename = file_name105    cached_file = os.path.abspath(os.path.join(model_dir, filename))106    if not os.path.exists(cached_file):107        print(f'Downloading: "{url}" to {cached_file}\n')108        download_url_to_file(url, cached_file, hash_prefix=None, progress=progress)109    return cached_file110 111 112# os.system("pip freeze")113ckpt_dir = Path('./ckpts')114if not ckpt_dir.exists():115	ckpt_dir.mkdir()116 117pretrain_model_url = {118	'vae': 'https://huggingface.co/ByteDance-Seed/SeedVR2-3B/resolve/main/ema_vae.pth',119	'dit': 'https://huggingface.co/ByteDance-Seed/SeedVR2-3B/resolve/main/seedvr2_ema_3b.pth',120    'pos_emb': 'https://huggingface.co/ByteDance-Seed/SeedVR2-3B/resolve/main/pos_emb.pt',121    'neg_emb': 'https://huggingface.co/ByteDance-Seed/SeedVR2-3B/resolve/main/neg_emb.pt',122    'apex': 'https://huggingface.co/ByteDance-Seed/SeedVR2-3B/resolve/main/apex-0.1-cp310-cp310-linux_x86_64.whl'123}124# download weights125if not os.path.exists('./ckpts/seedvr2_ema_3b.pth'):126	load_file_from_url(url=pretrain_model_url['dit'], model_dir='./ckpts/', progress=True, file_name=None)127if not os.path.exists('./ckpts/ema_vae.pth'):128	load_file_from_url(url=pretrain_model_url['vae'], model_dir='./ckpts/', progress=True, file_name=None)129if not os.path.exists('./pos_emb.pt'):130	load_file_from_url(url=pretrain_model_url['pos_emb'], model_dir='./', progress=True, file_name=None)131if not os.path.exists('./neg_emb.pt'):132	load_file_from_url(url=pretrain_model_url['neg_emb'], model_dir='./', progress=True, file_name=None)133if not os.path.exists('./apex-0.1-cp310-cp310-linux_x86_64.whl'):134	load_file_from_url(url=pretrain_model_url['apex'], model_dir='./', progress=True, file_name=None)135 136subprocess.run(shlex.split("pip install apex-0.1-cp310-cp310-linux_x86_64.whl"))137print(f"โœ… setup completed Apex")138 139# download images140torch.hub.download_url_to_file(141	'https://huggingface.co/datasets/Iceclear/SeedVR_VideoDemos/resolve/main/seedvr_videos_crf23/aigc1k/23_1_lq.mp4',142	'01.mp4')143torch.hub.download_url_to_file(144	'https://huggingface.co/datasets/Iceclear/SeedVR_VideoDemos/resolve/main/seedvr_videos_crf23/aigc1k/28_1_lq.mp4',145	'02.mp4')146torch.hub.download_url_to_file(147	'https://huggingface.co/datasets/Iceclear/SeedVR_VideoDemos/resolve/main/seedvr_videos_crf23/aigc1k/2_1_lq.mp4',148	'03.mp4')149 150def configure_sequence_parallel(sp_size):151    if sp_size > 1:152        init_sequence_parallel(sp_size)153 154@spaces.GPU(duration=100)155def configure_runner(sp_size):156    config_path = os.path.join('./configs_3b', 'main.yaml')157    config = load_config(config_path)158    runner = VideoDiffusionInfer(config)159    OmegaConf.set_readonly(runner.config, False)160    161    init_torch(cudnn_benchmark=False, timeout=datetime.timedelta(seconds=3600))162    configure_sequence_parallel(sp_size)163    runner.configure_dit_model(device="cuda", checkpoint='./ckpts/seedvr2_ema_3b.pth')164    runner.configure_vae_model()165    # Set memory limit.166    if hasattr(runner.vae, "set_memory_limit"):167        runner.vae.set_memory_limit(**runner.config.vae.memory_limit)168    return runner169 170@spaces.GPU(duration=100)171def generation_step(runner, text_embeds_dict, cond_latents):172    def _move_to_cuda(x):173        return [i.to(torch.device("cuda")) for i in x]174 175    noises = [torch.randn_like(latent) for latent in cond_latents]176    aug_noises = [torch.randn_like(latent) for latent in cond_latents]177    print(f"Generating with noise shape: {noises[0].size()}.")178    noises, aug_noises, cond_latents = sync_data((noises, aug_noises, cond_latents), 0)179    noises, aug_noises, cond_latents = list(180        map(lambda x: _move_to_cuda(x), (noises, aug_noises, cond_latents))181    )182    cond_noise_scale = 0.1183 184    def _add_noise(x, aug_noise):185        t = (186            torch.tensor([1000.0], device=torch.device("cuda"))187            * cond_noise_scale188        )189        shape = torch.tensor(x.shape[1:], device=torch.device("cuda"))[None]190        t = runner.timestep_transform(t, shape)191        print(192            f"Timestep shifting from"193            f" {1000.0 * cond_noise_scale} to {t}."194        )195        x = runner.schedule.forward(x, aug_noise, t)196        return x197 198    conditions = [199        runner.get_condition(200            noise,201            task="sr",202            latent_blur=_add_noise(latent_blur, aug_noise),203        )204        for noise, aug_noise, latent_blur in zip(noises, aug_noises, cond_latents)205    ]206 207    with torch.no_grad(), torch.autocast("cuda", torch.bfloat16, enabled=True):208        video_tensors = runner.inference(209            noises=noises,210            conditions=conditions,211            dit_offload=False,212            **text_embeds_dict,213        )214 215    samples = [216        (217            rearrange(video[:, None], "c t h w -> t c h w")218            if video.ndim == 3219            else rearrange(video, "c t h w -> t c h w")220        )221        for video in video_tensors222    ]223    del video_tensors224 225    return samples226 227@spaces.GPU(duration=100)228def generation_loop(video_path='./test_videos', seed=666, fps_out=12, batch_size=1, cfg_scale=1.0, cfg_rescale=0.0, sample_steps=1, res_h=1280, res_w=720, sp_size=1):229    runner = configure_runner(1)230 231    def _extract_text_embeds():232        # Text encoder forward.233        positive_prompts_embeds = []234        for texts_pos in tqdm(original_videos_local):235            text_pos_embeds = torch.load('pos_emb.pt')236            text_neg_embeds = torch.load('neg_emb.pt')237 238            positive_prompts_embeds.append(239                {"texts_pos": [text_pos_embeds], "texts_neg": [text_neg_embeds]}240            )241        gc.collect()242        torch.cuda.empty_cache()243        return positive_prompts_embeds244 245    def cut_videos(videos, sp_size):246        if videos.size(1) > 121:247            videos = videos[:, :121]248        t = videos.size(1)249        if t <= 4 * sp_size:250            print(f"Cut input video size: {videos.size()}")251            padding = [videos[:, -1].unsqueeze(1)] * (4 * sp_size - t + 1)252            padding = torch.cat(padding, dim=1)253            videos = torch.cat([videos, padding], dim=1)254            return videos255        if (t - 1) % (4 * sp_size) == 0:256            return videos257        else:258            padding = [videos[:, -1].unsqueeze(1)] * (259                4 * sp_size - ((t - 1) % (4 * sp_size))260            )261            padding = torch.cat(padding, dim=1)262            videos = torch.cat([videos, padding], dim=1)263            assert (videos.size(1) - 1) % (4 * sp_size) == 0264            return videos265 266    # classifier-free guidance267    runner.config.diffusion.cfg.scale = cfg_scale268    runner.config.diffusion.cfg.rescale = cfg_rescale269    # sampling steps270    runner.config.diffusion.timesteps.sampling.steps = sample_steps271    runner.configure_diffusion()272 273    # set random seed274    seed = seed % (2**32) # avoid over range275    set_seed(seed, same_across_ranks=True)276    os.makedirs('output/', exist_ok=True)277 278    # get test prompts279    original_videos = [video_path.split('/')[-1]]280 281    # divide the prompts into different groups282    original_videos_group = original_videos283    # store prompt mapping284    original_videos_local = original_videos_group285    original_videos_local = partition_by_size(original_videos_local, batch_size)286 287    # pre-extract the text embeddings288    positive_prompts_embeds = _extract_text_embeds()289 290    video_transform = Compose(291        [292            NaResize(293                resolution=(294                    res_h * res_w295                )296                ** 0.5,297                mode="area",298                # Upsample image, model only trained for high res.299                downsample_only=False,300            ),301            Lambda(lambda x: torch.clamp(x, 0.0, 1.0)),302            DivisibleCrop((16, 16)),303            Normalize(0.5, 0.5),304            Rearrange("t c h w -> c t h w"),305        ]306    )307 308    # generation loop309    for videos, text_embeds in tqdm(zip(original_videos_local, positive_prompts_embeds)):310        # read condition latents311        cond_latents = []312        for video in videos:313            media_type, _ = mimetypes.guess_type(video_path)314            is_image = media_type and media_type.startswith("image")315            is_video = media_type and media_type.startswith("video")316            if is_video:317                video = (318                    read_video(319                       os.path.join(video_path), output_format="TCHW"320                    )[0]321                    / 255.0322                )323                if video.size(0) > 121:324                    video = video[:121]325                print(f"Read video size: {video.size()}")326                output_dir = 'output/' + str(uuid.uuid4()) + '.mp4'327            else:328                video_transform = Compose(329                    [330                        NaResize(331                            resolution=(332                                2560 * 1440333                            )334                            ** 0.5,335                            mode="area",336                            # Upsample image, model only trained for high res.337                            downsample_only=False,338                        ),339                        Lambda(lambda x: torch.clamp(x, 0.0, 1.0)),340                        DivisibleCrop((16, 16)),341                        Normalize(0.5, 0.5),342                        Rearrange("t c h w -> c t h w"),343                    ]344                )345                346                img = Image.open(video_path).convert("RGB")347                img_tensor = T.ToTensor()(img).unsqueeze(0)  # (1, C, H, W)348                video = img_tensor.permute(0, 1, 2, 3)  # (T=1, C, H, W)349                print(f"Read Image size: {video.size()}")350                output_dir = 'output/' + str(uuid.uuid4()) + '.png'351            cond_latents.append(video_transform(video.to(torch.device("cuda"))))352 353        ori_lengths = [video.size(1) for video in cond_latents]354        input_videos = cond_latents355        if is_video:356            cond_latents = [cut_videos(video, sp_size) for video in cond_latents]357 358        # runner.dit.to("cpu")359        print(f"Encoding videos: {list(map(lambda x: x.size(), cond_latents))}")360        # runner.vae.to(torch.device("cuda"))361        cond_latents = runner.vae_encode(cond_latents)362        # runner.vae.to("cpu")363        # runner.dit.to(torch.device("cuda"))364 365        for i, emb in enumerate(text_embeds["texts_pos"]):366            text_embeds["texts_pos"][i] = emb.to(torch.device("cuda"))367        for i, emb in enumerate(text_embeds["texts_neg"]):368            text_embeds["texts_neg"][i] = emb.to(torch.device("cuda"))369 370        samples = generation_step(runner, text_embeds, cond_latents=cond_latents)371        # runner.dit.to("cpu")372        del cond_latents373 374        # dump samples to the output directory375        for path, input, sample, ori_length in zip(376            videos, input_videos, samples, ori_lengths377        ):378            if ori_length < sample.shape[0]:379                sample = sample[:ori_length]380            # color fix381            input = (382                rearrange(input[:, None], "c t h w -> t c h w")383                if input.ndim == 3384                else rearrange(input, "c t h w -> t c h w")385            )386            if use_colorfix:387                sample = wavelet_reconstruction(388                    sample.to("cpu"), input[: sample.size(0)].to("cpu")389                )390            else:391                sample = sample.to("cpu")392            sample = (393                rearrange(sample[:, None], "t c h w -> t h w c")394                if sample.ndim == 3395                else rearrange(sample, "t c h w -> t h w c")396            )397            sample = sample.clip(-1, 1).mul_(0.5).add_(0.5).mul_(255).round()398            sample = sample.to(torch.uint8).numpy()399 400            if is_image:401                mediapy.write_image(output_dir, sample[0])402            else:403                mediapy.write_video(404                    output_dir, sample, fps=fps_out405                )406 407        # print(f"Generated video size: {sample.shape}")408        gc.collect()409        torch.cuda.empty_cache()410        if is_image:411            return output_dir, None, output_dir412        else:413            return None, output_dir, output_dir414 415 416with gr.Blocks(title="SeedVR2: One-Step Video Restoration via Diffusion Adversarial Post-Training") as demo:417    # Top logo and title418    gr.HTML("""419        <div style='text-align:center; margin-bottom: 10px;'>420            <img src='https://huggingface.co/ByteDance-Seed/SeedVR2-3B/resolve/main/assets/seedvr_logo.png' style='height:40px;' alt='SeedVR logo'/>421        </div>422        <p><b>Official Gradio demo</b> for 423        <a href='https://github.com/ByteDance-Seed/SeedVR' target='_blank'>424        <b>SeedVR2: One-Step Video Restoration via Diffusion Adversarial Post-Training</b></a>.<br>425        ๐Ÿ”ฅ <b>SeedVR2</b> is a one-step image and video restoration algorithm for real-world and AIGC content.426        </p>427    """)428 429    # Interface430    with gr.Row():431        input_video = gr.File(label="Upload image or video", type="filepath")432        seed = gr.Number(label="Seeds", value=666)433        fps = gr.Number(label="fps", value=24)434 435    with gr.Row():436        output_video = gr.Video(label="Output_Video")437        output_image = gr.Image(label="Output_Image")438        download_link = gr.File(label="Download the output")439 440    run_button = gr.Button("Run")441    run_button.click(fn=generation_loop, inputs=[input_video, seed, fps], outputs=[output_image, output_video, download_link])442 443    # Examples444    gr.Examples(445        examples=[446            ["./01.mp4", 4, 24],447            ["./02.mp4", 4, 24],448            ["./03.mp4", 4, 24],449        ],450        inputs=[input_video, seed, fps]451    )452 453    # Article/Footer454    gr.HTML("""455        <hr>456        <p>If you find SeedVR helpful, please โญ the 457        <a href='https://github.com/ByteDance-Seed/SeedVR' target='_blank'>GitHub repository</a>:</p>458 459        <a href="https://github.com/ByteDance-Seed/SeedVR" target="_blank">460            <img src="https://img.shields.io/github/stars/ByteDance-Seed/SeedVR?style=social" alt="GitHub Stars">461        </a>462 463        <h4>Notice</h4>464        <p>This demo supports up to <b>720p and 121 frames for videos or 2k images</b>.  465        For other use cases (image restoration beyond 2K, video resolutions beyond 720p, etc), check the <a href='https://github.com/ByteDance-Seed/SeedVR' target='_blank'>GitHub repo</a>.</p>466 467        <h4>Limitations</h4>468        <p>May fail on heavy degradations or small-motion AIGC clips, causing oversharpening or poor restoration.</p>469 470        <h4>Citation</h4>471        <pre style="font-size: 12px;">472        @article{wang2025seedvr2,473            title={SeedVR2: One-Step Video Restoration via Diffusion Adversarial Post-Training},474            author={Wang, Jianyi and Lin, Shanchuan and Lin, Zhijie and Ren, Yuxi and Wei, Meng and Yue, Zongsheng and Zhou, Shangchen and Chen, Hao and Zhao, Yang and Yang, Ceyuan and Xiao, Xuefeng and Loy, Chen Change and Jiang, Lu},475            booktitle={arXiv preprint arXiv:2506.05301},476            year={2025}477        }478 479        @inproceedings{wang2025seedvr,480            title={SeedVR: Seeding Infinity in Diffusion Transformer Towards Generic Video Restoration},481            author={Wang, Jianyi and Lin, Zhijie and Wei, Meng and Zhao, Yang and Yang, Ceyuan and Loy, Chen Change and Jiang, Lu},482            booktitle={CVPR},483            year={2025}484        }485        </pre>486 487        <h4>License</h4>488        <p>Licensed under the 489        <a href="http://www.apache.org/licenses/LICENSE-2.0" target="_blank">Apache 2.0 License</a>.</p>490 491        <h4>Contact</h4>492        <p>Email: <b>iceclearwjy@gmail.com</b></p>493 494        <p>495        <a href="https://twitter.com/Iceclearwjy">496        <img src="https://img.shields.io/twitter/follow/Iceclearwjy?label=%40Iceclearwjy&style=social" alt="Twitter Follow">497        </a>498        <a href="https://github.com/IceClear">499        <img src="https://img.shields.io/github/followers/IceClear?style=social" alt="GitHub Follow">500        </a>501        </p>502 503        <p style="text-align:center;">504        <img src="https://visitor-badge.laobi.icu/badge?page_id=ByteDance-Seed/SeedVR" alt="visitors">505        </p>506    """)507 508demo.queue()509demo.launch()510