Refactor HVideoTool's configuration and project management: updated project handling in core/project.py, streamlined restoration settings in config.py, and improved documentation in CLAUDE.md. Removed unused parameters and enhanced type hints for better clarity.

This commit is contained in:
Leonid Pershin
2026-06-07 06:33:00 +03:00
parent 9c471ca701
commit ac02ca27a8
16 changed files with 81 additions and 105 deletions
+27 -25
View File
@@ -1,20 +1,22 @@
"""DeepMosaics restorer — real generative mosaic removal, in-process.
"""DeepMosaics restorers — real generative mosaic removal, in-process.
The DeepMosaics network code (GPL-3.0) is vendored under ``_deepmosaics/`` (see
its NOTICE/LICENSE). We load the models **once** and run the per-frame clean path
in-process — far faster than spawning a subprocess per frame (which reloaded the
models every time). Only the model *weights* are user-supplied.
The DeepMosaics network code (GPL-3.0) is vendored under ``_deepmosaics/`` (see its
NOTICE/LICENSE). We load the models **once** and run in-process — far faster than
spawning a subprocess per frame (which reloaded the models every time). Only the model
*weights* are user-supplied. Both engines locate the mosaic themselves (BiSeNet
``mosaic_position.pth``); detections are not passed to them.
Per-frame clean = DeepMosaics' ``cleanmosaic_img_server`` logic, reimplemented
here (so we don't pull in their video/ffmpeg modules):
locate mosaic (BiSeNet ``mosaic_position.pth``) → run the clean generator on the
crop → feather it back. DeepMosaics finds the mosaic itself; our detections are
used for navigation, not passed to it.
Two engines:
- :class:`DeepMosaicsRestorer` (per-frame): reproduces ``cleanmosaic_img_server`` —
locate mosaic → run the image generator on the crop → feather it back. Image weights
``clean_youknow_resnet_9blocks.pth``.
- :class:`DeepMosaicsVideoRestorer` (temporal/BVDNet): reproduces
``cleanmosaic_video_fusion`` — a window of neighbouring frames + recurrence, for
temporal coherence. Video weights ``clean_youknow_video.pth``; needs a contiguous
sequence (see :meth:`Restorer.restore_sequence`).
Setup (see README → Восстановление): download the **image** clean weights
``clean_youknow_resnet_9blocks.pth`` + ``mosaic_position.pth`` into one folder and
point the app at the clean-model file. The video model ``clean_youknow_video.pth``
(BVDNet) needs neighbour frames and does NOT work per-frame.
Setup (see README → Восстановление): drop the chosen ``clean_*.pth`` + ``mosaic_position.pth``
into one folder (``models/deepmosaics``) and pick it in the restore dialog.
"""
from __future__ import annotations
@@ -31,8 +33,8 @@ from .base import (
Cancelled,
DetGetter,
FrameGetter,
ResultSink,
Restorer,
ResultSink,
)
_VENDOR = Path(__file__).parent / "_deepmosaics"
@@ -94,9 +96,8 @@ def _netg_kind(model_name: str) -> str:
class DeepMosaicsRestorer(Restorer):
def __init__(
self,
deepmosaics_dir: str | None, # kept for factory/config compatibility (weights hint)
deepmosaics_dir: str | None, # optional extra dir to find mosaic_position.pth
model_path: str | None,
python_exe: str | None = None, # unused now (in-process)
gpu_id: str = "0",
) -> None:
if not model_path or not Path(model_path).is_file():
@@ -136,12 +137,13 @@ class DeepMosaicsRestorer(Restorer):
# Fall back to CPU when CUDA isn't available: DeepMosaics calls `.cuda()`
# whenever gpu_id != "-1", which raises "Torch not compiled with CUDA enabled"
# on a CPU-only torch build.
import torch # noqa: E402
import torch
if self._gpu != "-1" and not torch.cuda.is_available():
self._gpu = "-1"
from models import loadmodel, runmodel # type: ignore # noqa: E402
import util.image_processing as impro # type: ignore # noqa: E402
import util.image_processing as impro # type: ignore
from models import loadmodel, runmodel # type: ignore
self._runmodel = runmodel
self._impro = impro
@@ -206,7 +208,6 @@ class DeepMosaicsVideoRestorer(Restorer):
self,
deepmosaics_dir: str | None,
model_path: str | None,
python_exe: str | None = None, # unused (in-process); kept for factory parity
gpu_id: str = "0",
) -> None:
chosen: Path | None = None
@@ -244,13 +245,14 @@ class DeepMosaicsVideoRestorer(Restorer):
if str(_VENDOR) not in sys.path:
sys.path.insert(0, str(_VENDOR))
import torch # noqa: E402
import torch
if self._gpu != "-1" and not torch.cuda.is_available():
self._gpu = "-1" # CPU fallback (see DeepMosaicsRestorer for why)
from models import loadmodel, runmodel # type: ignore # noqa: E402
import util.data as data # type: ignore # noqa: E402
import util.image_processing as impro # type: ignore # noqa: E402
import util.data as data # type: ignore
import util.image_processing as impro # type: ignore
from models import loadmodel, runmodel # type: ignore
self._torch = torch
self._runmodel = runmodel
+3 -3
View File
@@ -20,20 +20,20 @@ if TYPE_CHECKING: # avoid importing AppConfig at runtime here (not needed)
from ...config import AppConfig
def build_restorer(name: str = "deepmosaics", config: "AppConfig | None" = None) -> Restorer:
def build_restorer(name: str = "deepmosaics", config: AppConfig | None = None) -> Restorer:
if config is None:
raise ValueError("Для DeepMosaics нужны настройки (config).")
if name == "deepmosaics":
from .deepmosaics import DeepMosaicsRestorer
return DeepMosaicsRestorer(
config.dm_dir, config.dm_model, config.dm_python, config.dm_gpu
config.dm_dir, config.dm_model, config.dm_gpu
)
if name == "deepmosaics_video":
from .deepmosaics import DeepMosaicsVideoRestorer
return DeepMosaicsVideoRestorer(
config.dm_dir, config.dm_model, config.dm_python, config.dm_gpu
config.dm_dir, config.dm_model, config.dm_gpu
)
if name == "lada":
raise ValueError(