Skip to content
FastVideo
Esc
↑↓navigate↵open⌘Jpreview
On this page

minimax_h3_taeh3

Optional, approximate MiniMax H3 tiny decoder for CUDA/CPU PyTorch.

Architecture and temporal mapping adapted from madebyollin/taehv at 62f7591f59dfbb4c3c02b7a621d180a9eeaba26c (MIT, Ollin Boer Bohan). This decoder consumes normalized diffusion latents; it does not use the full H3 VAE's latent mean/std or pixel denormalization.

fastvideo.models.vaes.minimax_h3_taeh3.init_logger

init_logger(name: str) -> _FastvideoLogger

The main purpose of this function is to ensure that loggers are retrieved in such a way that we can be sure the root fastvideo logger has already been configured.

fastvideo.models.vaes.minimax_h3_taeh3.ensure_taeh3_checkpoint

ensure_taeh3_checkpoint(checkpoint_path: str | Path | None = None) -> Path

Fetch only pinned weights, atomically; never download executable code.

Source
def ensure_taeh3_checkpoint(checkpoint_path: str | Path | None = None) -> Path:
    """Fetch only pinned weights, atomically; never download executable code."""
    if checkpoint_path is not None:
        path = Path(checkpoint_path).expanduser()
        if not path.is_file():
            raise FileNotFoundError(f"TAEH3 checkpoint not found: {path}")
        if path.suffix != ".safetensors":
            raise ValueError("The TAEH3 decoder requires a .safetensors checkpoint.")
        return path
    path = Path.home() / ".cache/fastvideo/taehv/taeh3.safetensors"
def verify(candidate: Path) -> None:
    hasher = hashlib.sha256()
    with candidate.open("rb") as handle:
        for chunk in iter(lambda: handle.read(1 << 20), b""):
            hasher.update(chunk)
    digest = hasher.hexdigest()
    if digest != TAEH3_SHA256:
        raise RuntimeError(f"TAEH3 checkpoint failed SHA-256 verification: {candidate}")

if path.exists():
    verify(path)
    return path
path.parent.mkdir(parents=True, exist_ok=True)
with tempfile.NamedTemporaryFile(dir=path.parent, suffix=".safetensors", delete=False) as temporary_file:
    temporary = Path(temporary_file.name)
try:
    with urllib.request.urlopen(TAEH3_URL, timeout=60) as response, temporary.open("wb") as handle:
        while chunk := response.read(1 << 20):
            handle.write(chunk)
    verify(temporary)
    temporary.replace(path)
finally:
    temporary.unlink(missing_ok=True)
logger.info("Cached TAEH3 checkpoint at %s", path)
return path</code></pre></details>

fastvideo.models.vaes.minimax_h3_taeh3.TorchTAEH3Decoder

TorchTAEH3Decoder(checkpoint_path: str | Path, *, dtype: torch.dtype = torch.float32)

Decode H3 NTCHW latents with bounded temporal feature memory.

Source
class TorchTAEH3Decoder:
    """Decode H3 NTCHW latents with bounded temporal feature memory."""
def __init__(self, checkpoint_path: str | Path, *, dtype: torch.dtype = torch.float32) -&gt; None:
    from safetensors.torch import load_file

    raw = load_file(str(checkpoint_path))
    actual = &#123;key for key in raw if key.startswith(&quot;decoder.&quot;)&#125;
    if actual != set(_EXPECTED_SHAPES):
        raise ValueError(f&quot;TAEH3 decoder keys mismatch: missing=&#123;set(_EXPECTED_SHAPES) - actual&#125;, &quot;
                         f&quot;unexpected=&#123;actual - set(_EXPECTED_SHAPES)&#125;&quot;)
    self.dtype = dtype
    self.weights: dict&#91;str, torch.Tensor&#93; = &#123;&#125;
    for key, shape in _EXPECTED_SHAPES.items():
        value = raw&#91;key&#93;
        if tuple(value.shape) != shape:
            raise ValueError(f&quot;TAEH3 weight &#123;key&#125; has shape &#123;tuple(value.shape)&#125;, expected &#123;shape&#125;&quot;)
        self.weights&#91;key&#93; = value.detach().to(dtype=dtype).contiguous()

def to(self, device: torch.device) -&gt; &quot;TorchTAEH3Decoder&quot;:
    self.weights = &#123;key: value.to(device=device, non_blocking=True) for key, value in self.weights.items()&#125;
    return self

def _conv(self, x: torch.Tensor, name: str) -&gt; torch.Tensor:
    weight = self.weights&#91;f&quot;&#123;name&#125;.weight&quot;&#93;
    bias = self.weights.get(f&quot;&#123;name&#125;.bias&quot;)
    padding = weight.shape&#91;-1&#93; // 2
    return F.conv2d(x, weight, bias, padding=padding)

def _chunk(self, x: torch.Tensor, memory: dict&#91;int, torch.Tensor&#93;) -&gt; torch.Tensor:
    n, t, c, h, w = x.shape
    x = F.relu(self._conv(torch.tanh(x.reshape(n * t, c, h, w) / 3.0) * 3.0, &quot;decoder.1&quot;))
    for indices, grow, projection, stride in (((3, 4, 5), 7, 8, 1), ((9, 10, 11), 13, 14, 2), ((15, 16, 17), 19, 20,
                                                                                               2)):
        for index in indices:
            nt, c, h, w = x.shape
            sequence = x.reshape(n, -1, c, h, w)
            previous = memory.get(index)
            if previous is None:
                previous = torch.zeros_like(sequence&#91;:, :1&#93;)
            past = torch.cat(&#91;previous, sequence&#91;:, :-1&#93;&#93;, dim=1).reshape_as(x)
            memory&#91;index&#93; = sequence&#91;:, -1:&#93;.contiguous()
            y = torch.cat(&#91;x, past&#93;, dim=1)
            for layer in (0, 2, 4):
                y = self._conv(y, f&quot;decoder.&#123;index&#125;.conv.&#123;layer&#125;&quot;)
                if layer != 4:
                    y = F.relu(y)
            x = F.relu(x + y)
        nt, c_pre, h, w = x.shape
        x = F.interpolate(x, scale_factor=2, mode=&quot;nearest&quot;)
        x = self._conv(x, f&quot;decoder.&#123;grow&#125;.conv&quot;)
        x = x.reshape(nt, stride, c_pre, h * 2, w * 2).reshape(nt * stride, c_pre, h * 2, w * 2)
        x = self._conv(x, f&quot;decoder.&#123;projection&#125;&quot;)
    x = self._conv(F.relu(x), &quot;decoder.22&quot;)
    x = F.pixel_shuffle(x, 2).clamp(0, 1)
    nt, c, h, w = x.shape
    return x.reshape(n, -1, c, h, w)

def decode_ntchw(self, latents: torch.Tensor, *, chunk_size: int = 5) -&gt; torch.Tensor:
    &quot;&quot;&quot;Return NTCHW RGB in &#91;0, 1&#93; for H3&#x27;s valid 5*k-3 latent lengths.&quot;&quot;&quot;
    if latents.ndim != 5 or latents.shape&#91;2&#93; != 24 or min(latents.shape) &lt;= 0:
        raise ValueError(f&quot;Expected nonempty NTCHW H3 latents with 24 channels, got &#123;tuple(latents.shape)&#125;&quot;)
    if latents.shape&#91;1&#93; % 5 != 2:
        raise ValueError(&quot;H3 latent time must be 5*k-3, for example 2, 7, or 37.&quot;)
    if chunk_size &lt; 1:
        raise ValueError(&quot;TAEH3 chunk_size must be positive.&quot;)
    x = latents.to(dtype=self.dtype)
    memory: dict&#91;int, torch.Tensor&#93; = &#123;&#125;
    frames: list&#91;torch.Tensor&#93; = &#91;&#93;
    for start in range(0, x.shape&#91;1&#93;, chunk_size):
        decoded = self._chunk(x&#91;:, start:start + chunk_size&#93;, memory)
        keep = &#91;i for i in range(decoded.shape&#91;1&#93;) if (start * 4 + i) % 20 &gt;= 3&#93;
        frames.append(decoded&#91;:, keep&#93;)
    return torch.cat(frames, dim=1)</code></pre></details>

fastvideo.models.vaes.minimax_h3_taeh3.TorchTAEH3Decoder.to

to(device: torch.device) -> 'TorchTAEH3Decoder'
Source
def to(self, device: torch.device) -> "TorchTAEH3Decoder":
    self.weights = {key: value.to(device=device, non_blocking=True) for key, value in self.weights.items()}
    return self

fastvideo.models.vaes.minimax_h3_taeh3.TorchTAEH3Decoder.decode_ntchw

decode_ntchw(latents: torch.Tensor, *, chunk_size: int = 5) -> torch.Tensor

Return NTCHW RGB in [0, 1] for H3's valid 5*k-3 latent lengths.

Source
def decode_ntchw(self, latents: torch.Tensor, *, chunk_size: int = 5) -> torch.Tensor:
    """Return NTCHW RGB in [0, 1] for H3's valid 5*k-3 latent lengths."""
    if latents.ndim != 5 or latents.shape[2] != 24 or min(latents.shape) <= 0:
        raise ValueError(f"Expected nonempty NTCHW H3 latents with 24 channels, got {tuple(latents.shape)}")
    if latents.shape[1] % 5 != 2:
        raise ValueError("H3 latent time must be 5*k-3, for example 2, 7, or 37.")
    if chunk_size < 1:
        raise ValueError("TAEH3 chunk_size must be positive.")
    x = latents.to(dtype=self.dtype)
    memory: dict[int, torch.Tensor] = {}
    frames: list[torch.Tensor] = []
    for start in range(0, x.shape[1], chunk_size):
        decoded = self._chunk(x[:, start:start + chunk_size], memory)
        keep = [i for i in range(decoded.shape[1]) if (start * 4 + i) % 20 >= 3]
        frames.append(decoded[:, keep])
    return torch.cat(frames, dim=1)

fastvideo.models.vaes.minimax_h3_taeh3.decode_ncthw_latents_taeh3

decode_ncthw_latents_taeh3(latents: torch.Tensor, *, device: torch.device, checkpoint_path: str | Path | None = None, chunk_size: int = 5, dtype: torch.dtype = torch.float32) -> torch.Tensor

Decode normalized NCTHW diffusion latents into NCTHW RGB in [0, 1].

Source
def decode_ncthw_latents_taeh3(
    latents: torch.Tensor,
    *,
    device: torch.device,
    checkpoint_path: str | Path | None = None,
    chunk_size: int = 5,
    dtype: torch.dtype = torch.float32,
) -> torch.Tensor:
    """Decode normalized NCTHW diffusion latents into NCTHW RGB in [0, 1]."""
    if latents.ndim != 5:
        raise ValueError(f"Expected NCTHW latents, got {tuple(latents.shape)}")
    checkpoint = ensure_taeh3_checkpoint(checkpoint_path)
    cache_key = (str(checkpoint), str(device), str(dtype))
    decoder = _DECODER_CACHE.get(cache_key)
    if decoder is None:
        decoder = TorchTAEH3Decoder(checkpoint, dtype=dtype).to(device)
        _DECODER_CACHE[cache_key] = decoder
    ntchw = latents.to(device=device, dtype=dtype).permute(0, 2, 1, 3, 4).contiguous()
    rgb = decoder.decode_ntchw(ntchw, chunk_size=chunk_size)
    return rgb.permute(0, 2, 1, 3, 4).contiguous()

fastvideo.models.vaes.minimax_h3_taeh3.taeh3_decoded_pixel_shape

taeh3_decoded_pixel_shape(latent_shape: tuple[int, ...] | torch.Size) -> tuple[int, int, int, int, int]

Return NCTHW pixel shape for H3 TAEH3 (16x spatial, drop 3 of every 20 raw frames).

Source
def taeh3_decoded_pixel_shape(latent_shape: tuple[int, ...] | torch.Size) -> tuple[int, int, int, int, int]:
    """Return NCTHW pixel shape for H3 TAEH3 (16x spatial, drop 3 of every 20 raw frames)."""
    if len(latent_shape) != 5:
        raise ValueError(f"MiniMax-H3 latents must be five-dimensional, got shape {tuple(latent_shape)}.")
    batch, channels, latent_frames, latent_height, latent_width = map(int, latent_shape)
    if channels != 24:
        raise ValueError(f"TAEH3 latents must have 24 channels, got {channels}.")
    if latent_frames % 5 != 2:
        raise ValueError(f"H3 latent time must be 5*k-3, got {latent_frames}.")
    raw_frames = latent_frames * 4
    kept = sum(1 for index in range(raw_frames) if index % 20 >= 3)
    return (batch, 3, kept, latent_height * 16, latent_width * 16)

Was this page helpful?