CoolFace
Apppublic

SubstanceSHIFT/SeedVR2-3B

sourceHugging Faceapache-2.0updated 1y agoView on Hugging Face
0likes
inference_seedvr_7b.py325 linesDownload Raw Back to projects
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.14 15import os16import torch17import mediapy18from einops import rearrange19from omegaconf import OmegaConf20print(os.getcwd())21import datetime22from tqdm import tqdm23from models.dit import na24import gc25 26from data.image.transforms.divisible_crop import DivisibleCrop27from data.image.transforms.na_resize import NaResize28from data.video.transforms.rearrange import Rearrange29if os.path.exists("./projects/video_diffusion_sr/color_fix.py"):30    from projects.video_diffusion_sr.color_fix import wavelet_reconstruction31    use_colorfix=True32else:33    use_colorfix = False34    print('Note!!!!!! Color fix is not avaliable!')35from torchvision.transforms import Compose, Lambda, Normalize36from torchvision.io.video import read_video37import argparse38 39 40from common.distributed import (41    get_device,42    init_torch,43)44 45from common.distributed.advanced import (46    get_data_parallel_rank,47    get_data_parallel_world_size,48    get_sequence_parallel_rank,49    get_sequence_parallel_world_size,50    init_sequence_parallel,51)52 53from projects.video_diffusion_sr.infer import VideoDiffusionInfer54from common.config import load_config55from common.distributed.ops import sync_data56from common.seed import set_seed57from common.partition import partition_by_groups, partition_by_size58 59 60def configure_sequence_parallel(sp_size):61    if sp_size > 1:62        init_sequence_parallel(sp_size)63 64def configure_runner(sp_size):65    config_path = os.path.join('./configs_7b', 'main.yaml')66    config = load_config(config_path)67    runner = VideoDiffusionInfer(config)68    OmegaConf.set_readonly(runner.config, False)69    70    init_torch(cudnn_benchmark=False, timeout=datetime.timedelta(seconds=3600))71    configure_sequence_parallel(sp_size)72    runner.configure_dit_model(device="cuda", checkpoint='./ckpts/seedvr_ema_7b.pth')73    runner.configure_vae_model()74    # Set memory limit.75    if hasattr(runner.vae, "set_memory_limit"):76        runner.vae.set_memory_limit(**runner.config.vae.memory_limit)77    return runner78 79def generation_step(runner, text_embeds_dict, cond_latents):80    def _move_to_cuda(x):81        return [i.to(get_device()) for i in x]82 83    noises = [torch.randn_like(latent) for latent in cond_latents]84    aug_noises = [torch.randn_like(latent) for latent in cond_latents]85    print(f"Generating with noise shape: {noises[0].size()}.")86    noises, aug_noises, cond_latents = sync_data((noises, aug_noises, cond_latents), 0)87    noises, aug_noises, cond_latents = list(88        map(lambda x: _move_to_cuda(x), (noises, aug_noises, cond_latents))89    )90    cond_noise_scale = 0.191 92    def _add_noise(x, aug_noise):93        t = (94            torch.tensor([1000.0], device=get_device())95            * cond_noise_scale96        )97        shape = torch.tensor(x.shape[1:], device=get_device())[None]98        t = runner.timestep_transform(t, shape)99        print(100            f"Timestep shifting from"101            f" {1000.0 * cond_noise_scale} to {t}."102        )103        x = runner.schedule.forward(x, aug_noise, t)104        return x105 106    conditions = [107        runner.get_condition(108            noise,109            task="sr",110            latent_blur=_add_noise(latent_blur, aug_noise),111        )112        for noise, aug_noise, latent_blur in zip(noises, aug_noises, cond_latents)113    ]114 115    with torch.no_grad(), torch.autocast("cuda", torch.bfloat16, enabled=True):116        video_tensors = runner.inference(117            noises=noises,118            conditions=conditions,119            dit_offload=True,120            **text_embeds_dict,121        )122 123    samples = [124        (125            rearrange(video[:, None], "c t h w -> t c h w")126            if video.ndim == 3127            else rearrange(video, "c t h w -> t c h w")128        )129        for video in video_tensors130    ]131    del video_tensors132 133    return samples134 135def generation_loop(runner, video_path='./test_videos', output_dir='./results', batch_size=1, cfg_scale=6.5, cfg_rescale=0.0, sample_steps=50, seed=666, res_h=1280, res_w=720, sp_size=1):136 137    def _build_pos_and_neg_prompt():138        # read positive prompt139        positive_text = "Cinematic, High Contrast, highly detailed, taken using a Canon EOS R camera, \140        hyper detailed photo - realistic maximum detail, 32k, Color Grading, ultra HD, extreme meticulous detailing, \141        skin pore detailing, hyper sharpness, perfect without deformations."142        # read negative prompt143        negative_text = "painting, oil painting, illustration, drawing, art, sketch, oil painting, cartoon, \144        CG Style, 3D render, unreal engine, blurring, dirty, messy, worst quality, low quality, frames, watermark, \145        signature, jpeg artifacts, deformed, lowres, over-smooth"146        return positive_text, negative_text147 148    def _build_test_prompts(video_path):149        positive_text, negative_text = _build_pos_and_neg_prompt()150        original_videos = []151        prompts = {}152        video_list = os.listdir(video_path)153        for f in video_list:154            if f.endswith(".mp4"):155                original_videos.append(f)156                prompts[f] = positive_text157        print(f"Total prompts to be generated: {len(original_videos)}")158        return original_videos, prompts, negative_text159 160    def _extract_text_embeds():161        # Text encoder forward.162        positive_prompts_embeds = []163        for texts_pos in tqdm(original_videos_local):164            text_pos_embeds = torch.load('pos_emb.pt')165            text_neg_embeds = torch.load('neg_emb.pt')166 167            positive_prompts_embeds.append(168                {"texts_pos": [text_pos_embeds], "texts_neg": [text_neg_embeds]}169            )170        gc.collect()171        torch.cuda.empty_cache()172        return positive_prompts_embeds173 174    def cut_videos(videos, sp_size):175        t = videos.size(1)176        if t <= 4 * sp_size:177            print(f"Cut input video size: {videos.size()}")178            padding = [videos[:, -1].unsqueeze(1)] * (4 * sp_size - t + 1)179            padding = torch.cat(padding, dim=1)180            videos = torch.cat([videos, padding], dim=1)181            return videos182        if (t - 1) % (4 * sp_size) == 0:183            return videos184        else:185            padding = [videos[:, -1].unsqueeze(1)] * (186                4 * sp_size - ((t - 1) % (4 * sp_size))187            )188            padding = torch.cat(padding, dim=1)189            videos = torch.cat([videos, padding], dim=1)190            assert (videos.size(1) - 1) % (4 * sp_size) == 0191            return videos192 193    # classifier-free guidance194    runner.config.diffusion.cfg.scale = cfg_scale195    runner.config.diffusion.cfg.rescale = cfg_rescale196    # sampling steps197    runner.config.diffusion.timesteps.sampling.steps = sample_steps198    runner.configure_diffusion()199 200    # set random seed201    set_seed(seed, same_across_ranks=True)202    os.makedirs(output_dir, exist_ok=True)203    tgt_path = output_dir204 205    # get test prompts206    original_videos, _, _ = _build_test_prompts(video_path)207 208    # divide the prompts into different groups209    original_videos_group = partition_by_groups(210        original_videos,211        get_data_parallel_world_size() // get_sequence_parallel_world_size(),212    )213    # store prompt mapping214    original_videos_local = original_videos_group[215        get_data_parallel_rank() // get_sequence_parallel_world_size()216    ]217    original_videos_local = partition_by_size(original_videos_local, batch_size)218 219    # pre-extract the text embeddings220    positive_prompts_embeds = _extract_text_embeds()221 222    video_transform = Compose(223        [224            NaResize(225                resolution=(226                    res_h * res_w227                )228                ** 0.5,229                mode="area",230                # Upsample image, model only trained for high res.231                downsample_only=False,232            ),233            Lambda(lambda x: torch.clamp(x, 0.0, 1.0)),234            DivisibleCrop((16, 16)),235            Normalize(0.5, 0.5),236            Rearrange("t c h w -> c t h w"),237        ]238    )239 240    # generation loop241    for videos, text_embeds in tqdm(zip(original_videos_local, positive_prompts_embeds)):242        # read condition latents243        cond_latents = []244        for video in videos:245            video = (246                read_video(247                   os.path.join(video_path, video), output_format="TCHW"248                )[0]249                / 255.0250            )251            print(f"Read video size: {video.size()}")252            cond_latents.append(video_transform(video.to(get_device())))253 254        ori_lengths = [video.size(1) for video in cond_latents]255        input_videos = cond_latents256        cond_latents = [cut_videos(video, sp_size) for video in cond_latents]257 258        runner.dit.to("cpu")259        print(f"Encoding videos: {list(map(lambda x: x.size(), cond_latents))}")260        runner.vae.to(get_device())261        cond_latents = runner.vae_encode(cond_latents)262        runner.vae.to("cpu")263        runner.dit.to(get_device())264 265        for i, emb in enumerate(text_embeds["texts_pos"]):266            text_embeds["texts_pos"][i] = emb.to(get_device())267        for i, emb in enumerate(text_embeds["texts_neg"]):268            text_embeds["texts_neg"][i] = emb.to(get_device())269 270        samples = generation_step(runner, text_embeds, cond_latents=cond_latents)271        runner.dit.to("cpu")272        del cond_latents273 274        # dump samples to the output directory275        if get_sequence_parallel_rank() == 0:276            for path, input, sample, ori_length in zip(277                videos, input_videos, samples, ori_lengths278            ):279                if ori_length < sample.shape[0]:280                    sample = sample[:ori_length]281                filename = os.path.join(tgt_path, os.path.basename(path))282                # color fix283                input = (284                    rearrange(input[:, None], "c t h w -> t c h w")285                    if input.ndim == 3286                    else rearrange(input, "c t h w -> t c h w")287                )288                if use_colorfix:289                    sample = wavelet_reconstruction(290                        sample.to("cpu"), input[: sample.size(0)].to("cpu")291                    )292                else:293                    sample = sample.to("cpu")294                sample = (295                    rearrange(sample[:, None], "t c h w -> t h w c")296                    if sample.ndim == 3297                    else rearrange(sample, "t c h w -> t h w c")298                )299                sample = sample.clip(-1, 1).mul_(0.5).add_(0.5).mul_(255).round()300                sample = sample.to(torch.uint8).numpy()301 302                if sample.shape[0] == 1:303                    mediapy.write_image(filename, sample.squeeze(0))304                else:305                    mediapy.write_video(306                        filename, sample, fps=24307                    )308        gc.collect()309        torch.cuda.empty_cache()310 311if __name__ == "__main__":312    parser = argparse.ArgumentParser() 313    parser.add_argument("--video_path", type=str, default="./test_videos")314    parser.add_argument("--output_dir", type=str, default="./results")315    parser.add_argument("--cfg_scale", type=float, default=6.5)316    parser.add_argument("--sample_steps", type=int, default=50)317    parser.add_argument("--seed", type=int, default=666)318    parser.add_argument("--res_h", type=int, default=720)319    parser.add_argument("--res_w", type=int, default=1280)320    parser.add_argument("--sp_size", type=int, default=1)321    args = parser.parse_args()322 323    runner = configure_runner(args.sp_size)324    generation_loop(runner, **vars(args))325