Enhance HVideoTool's detection and restoration features: added support for tracking the model used in detections, improved temporal coherence by allowing the use of already-restored frames in the restoration process, and updated the UI to reflect these changes with new indicators and configuration options. Documentation in CLAUDE.md has been updated accordingly.

This commit is contained in:
Leonid Pershin
2026-06-07 08:40:57 +03:00
parent 7a225efa85
commit cabb4e3d3d
11 changed files with 168 additions and 27 deletions
+29 -3
View File
@@ -209,7 +209,12 @@ class DeepMosaicsVideoRestorer(Restorer):
deepmosaics_dir: str | None,
model_path: str | None,
gpu_id: str = "0",
feed_restored: bool = True,
) -> None:
# When True, already-restored PAST frames are fed into the temporal window
# (instead of the original mosaic frames). Future neighbours and the centre
# frame stay original — they aren't restored yet / are what we're cleaning.
self._feed_restored = feed_restored
chosen: Path | None = None
if model_path and Path(model_path).is_file() and "video" in Path(model_path).name.lower():
chosen = Path(model_path)
@@ -299,6 +304,18 @@ class DeepMosaicsVideoRestorer(Restorer):
self._ensure_loaded()
torch, data, impro, opt = self._torch, self._data, self._impro, self._opt
N, T, S, SZ = self._N, self._T, self._S, self._INPUT_SIZE
reach = N * S # how far back/forward the window samples (frames)
# Rolling cache of already-restored frames, used as window neighbours when
# ``feed_restored`` is on. Only the last ``reach`` frames are ever needed.
restored: dict[int, np.ndarray] = {}
def remember(idx: int, frame: np.ndarray) -> None:
if not self._feed_restored:
return
restored[idx] = frame
for old in [k for k in restored if k < idx - reach]:
restored.pop(old, None)
previous = None # recurrent state: the network's previous output (a tensor)
for i in range(count):
@@ -307,13 +324,20 @@ class DeepMosaicsVideoRestorer(Restorer):
img_origin = get_frame(i)
x, y, size, mask = self._runmodel.get_mosaic_position(img_origin, self._netM, opt)
if size <= 50:
emit(i, img_origin.copy()) # no mosaic here; recurrence carries over
clean = img_origin.copy() # no mosaic here; recurrence carries over
emit(i, clean)
remember(i, clean)
continue
stream = []
for k in range(T):
j = min(max(i + (k - N) * S, 0), count - 1) # clamp window to range edges
frame = img_origin if j == i else get_frame(j)
if j == i:
frame = img_origin
elif self._feed_restored and j < i and j in restored:
frame = restored[j] # already-restored past neighbour
else:
frame = get_frame(j) # original (future neighbour / not yet cached)
crop = frame[y - size:y + size, x - size:x + size]
stream.append(impro.resize(crop, SZ)[:, :, ::-1]) # BGR→RGB, SZ×SZ
@@ -326,4 +350,6 @@ class DeepMosaicsVideoRestorer(Restorer):
pred = self._netG(tensor, previous)
previous = pred
img_fake = data.tensor2im(pred, rgb2bgr=True)
emit(i, impro.replace_mosaic(img_origin.copy(), img_fake, mask, x, y, size, opt.no_feather))
result = impro.replace_mosaic(img_origin.copy(), img_fake, mask, x, y, size, opt.no_feather)
emit(i, result)
remember(i, result)