Spaces:
Running on Zero
Running on Zero
Download inference/preprocessing/stream.py from hugging-apps/flashvsr-plus: direct link, hf CLI and curl.
- Browser
- Download file 4.22 kB
-
https://huggingface.co/spaces/hugging-apps/flashvsr-plus/resolve/main/inference/preprocessing/stream.py
- Command line
-
hf download hf://spaces/hugging-apps/flashvsr-plus/inference/preprocessing/stream.py
-
curl -L -o stream.py https://huggingface.co/spaces/hugging-apps/flashvsr-plus/resolve/main/inference/preprocessing/stream.py
4.22 kB
| from __future__ import annotations | |
| from collections.abc import Iterator | |
| import torch | |
| from .padding import target_frame_count | |
| class TemporalVideoStream: | |
| """Expand a source timeline on demand and release consumed LQ frames. | |
| Only EOF introduces temporal padding. Frame indices refer to the expanded | |
| model timeline, including its unique first frame. | |
| """ | |
| def __init__(self, chunks: Iterator[torch.Tensor], compression: int, model_compression: int = 4): | |
| if compression not in (1, 2, 4) or model_compression % compression: | |
| raise ValueError("invalid temporal compression") | |
| self.chunks = iter(chunks) | |
| self.compression = compression | |
| self.repeat_factor = model_compression // compression | |
| self.source_frames = 0 | |
| self.effective_frames = 0 | |
| self.expanded_frames = 0 | |
| self.eof = False | |
| self.buffer = None | |
| self.offset = 0 | |
| self.last_frame = None | |
| self.max_buffer_frames = 0 | |
| def _append(self, value: torch.Tensor) -> None: | |
| if not value.shape[2]: | |
| return | |
| self.buffer = value.clone() if self.buffer is None else torch.cat((self.buffer, value), dim=2) | |
| self.expanded_frames += value.shape[2] | |
| self.max_buffer_frames = max(self.max_buffer_frames, self.buffer.shape[2]) | |
| def ensure(self, stop: int) -> bool: | |
| while self.expanded_frames < stop and not self.eof: | |
| try: | |
| value = next(self.chunks) | |
| except StopIteration: | |
| self.eof = True | |
| if not self.source_frames: | |
| raise ValueError("No frames decoded from input video") | |
| self.effective_frames = target_frame_count(self.source_frames, self.compression) | |
| missing = (self.effective_frames - self.source_frames) * self.repeat_factor | |
| self._append(self.last_frame.repeat(1, 1, missing, 1, 1)) | |
| break | |
| if value.ndim != 5 or value.shape[:2] != (1, 3) or not value.shape[2]: | |
| raise ValueError("Input chunks must be nonempty [1,3,F,H,W] tensors") | |
| if self.last_frame is not None and value.shape[-2:] != self.last_frame.shape[-2:]: | |
| raise ValueError("Input chunk dimensions changed") | |
| first = self.source_frames == 0 | |
| self.source_frames += value.shape[2] | |
| self.last_frame = value[:, :, -1:].clone() | |
| if first: | |
| expanded = torch.cat((value[:, :, :1], value[:, :, 1:].repeat_interleave(self.repeat_factor, dim=2)), dim=2) | |
| else: | |
| expanded = value.repeat_interleave(self.repeat_factor, dim=2) | |
| self._append(expanded) | |
| return self.expanded_frames >= stop | |
| def window(self, start: int, stop: int) -> torch.Tensor: | |
| self.ensure(stop) | |
| if start < self.offset or stop <= start: | |
| raise ValueError("Requested frames have already been released or window is empty") | |
| value = self.buffer[:, :, start - self.offset:stop - self.offset] | |
| if not value.shape[2]: | |
| value = self.last_frame[:, :, :0] | |
| missing = stop - start - value.shape[2] | |
| if missing: | |
| value = torch.cat((value, self.last_frame.repeat(1, 1, missing, 1, 1)), dim=2) | |
| return value.clone() | |
| def release_before(self, frame: int) -> None: | |
| frame = min(frame, self.expanded_frames) | |
| if frame > self.offset: | |
| self.buffer = self.buffer[:, :, frame - self.offset:].clone() | |
| self.offset = frame | |
| def projection_chunks(self, chunk_latents: int, model_compression: int): | |
| index = 0 | |
| stride = chunk_latents * model_compression | |
| while self.ensure(1 + index * stride): | |
| start = 0 if index == 0 else 1 + index * stride | |
| stop = 1 + (index + 1) * stride | |
| value = self.window(start, stop) | |
| last = self.eof and self.expanded_frames < stop | |
| yield index, value, start, last | |
| index += 1 | |
| def close(self) -> None: | |
| close = getattr(self.chunks, "close", None) | |
| if close is not None: | |
| close() | |
| self.buffer = None | |
| self.last_frame = None | |