lip-forcing / lipforcing /methods /reward /taehv_decoder.py
multimodalart's picture
multimodalart HF Staff
Initial Lip Forcing 14B streaming demo
9368ee7 verified
Raw
History Blame Contribute Delete
2.54 kB
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""TAEHV decoder wrapper that mimics WanVideoVAE.decode() for the Re-DMD reward path.
WanVideoVAE.decode contract (what _decode_gen_to_pixels consumes):
input: list of [C=16, T_lat, H, W] float tensors
output: [N, 3, T_pix, H_pix, W_pix] float32 tensor in [-1, 1], NCTHW
TAEHV's decode_video contract:
input: NTCHW tensor in the raw diffusion latent space (no mean/std scaling)
output: NTCHW RGB tensor in [0, 1], already trimmed to (T_lat - 1) * t_upscale + 1 frames
via the built-in frames_to_trim slice at the end of decode_video.
Transformation applied here:
1. stack list -> [N, 16, T_lat, H, W] and permute to NTCHW: [N, T_lat, 16, H, W]
2. run TAEHV.decode_video(parallel=True, show_progress_bar=False)
3. rescale [0, 1] -> [-1, 1] via x.mul(2).sub(1)
4. permute back to NCTHW: [N, 3, T_pix, H_pix, W_pix]
5. .float() for downstream compatibility (the scorer expects float32)
"""
from __future__ import annotations
from typing import List, Optional
import torch
from lipforcing.methods.reward.taehv import TAEHV
class TAEHVDecoderWrapper:
"""Drop-in WanVideoVAE.decode replacement backed by TAEHV.
Runs in fp16 internally for speed; returns fp32 to match WanVideoVAE's
float contract with the sync-C scorer (which re-casts to float anyway).
"""
def __init__(self, checkpoint_path: str, device: str = "cuda"):
self.device = device
self._taehv = TAEHV(checkpoint_path=checkpoint_path).to(device, torch.float16).eval()
for p in self._taehv.parameters():
p.requires_grad_(False)
@torch.no_grad()
def decode(self, latents_list: List[torch.Tensor], device: Optional[str] = None) -> torch.Tensor:
target_device = device if device is not None else self.device
# Stack list of [C, T, H, W] into [N, C, T, H, W], then NCTHW -> NTCHW.
batched = torch.stack([lat.to(target_device, dtype=torch.float16) for lat in latents_list], dim=0)
batched = batched.permute(0, 2, 1, 3, 4).contiguous() # [N, T_lat, C, H, W]
vid = self._taehv.decode_video(batched, parallel=True, show_progress_bar=False)
# vid: [N, T_pix, 3, H_pix, W_pix] in [0, 1] — already trimmed by decode_video.
vid = vid.mul(2.0).sub(1.0) # [0, 1] -> [-1, 1], match WanVideoVAE contract
return vid.permute(0, 2, 1, 3, 4).float().contiguous() # [N, 3, T_pix, H_pix, W_pix]