Enhance documentation and UI for HVideoTool: added restoration feature for detected regions, updated layout for collection controls, and improved navigation bar. Clarified tool capabilities and limitations in README and CLAUDE.md.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Restorer interface — "un-censor" detected regions of an image.
|
||||
|
||||
A Restorer takes an image plus the detected censored regions and returns a new
|
||||
image with those regions reconstructed/filled. This mirrors the ``Detector``
|
||||
abstraction so different engines (classic inpaint now; a generative model like
|
||||
DeepMosaics / LADA later) plug in behind the same interface.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..detection.types import Detection
|
||||
|
||||
|
||||
class Restorer(ABC):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return type(self).__name__
|
||||
|
||||
@abstractmethod
|
||||
def restore(self, image: np.ndarray, detections: list[Detection]) -> np.ndarray:
|
||||
"""Return a copy of ``image`` with the detected regions reconstructed."""
|
||||
raise NotImplementedError
|
||||
@@ -0,0 +1,22 @@
|
||||
"""Restorer factory: build a Restorer by name.
|
||||
|
||||
Currently only the cv2 inpaint baseline is wired. Generative engines
|
||||
(DeepMosaics / LADA BasicVSR++) are placeholders — they need model weights and a
|
||||
CUDA GPU, and raise a clear, actionable error until integrated. See README.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .base import Restorer
|
||||
from .inpaint import InpaintRestorer
|
||||
|
||||
|
||||
def build_restorer(name: str = "inpaint", model_path: str | None = None) -> Restorer:
|
||||
if name == "inpaint":
|
||||
return InpaintRestorer()
|
||||
if name in ("deepmosaics", "lada"):
|
||||
raise ValueError(
|
||||
"Генеративное восстановление пока не подключено.\n"
|
||||
"Нужна модель (DeepMosaics / LADA) и GPU (CUDA). См. README → Восстановление."
|
||||
)
|
||||
raise ValueError(f"Неизвестный режим восстановления: {name!r}")
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Classic inpainting restorer (cv2) — the always-available baseline.
|
||||
|
||||
HONEST LIMITATION: cv2 inpainting fills the masked region by propagating
|
||||
surrounding pixels. It removes the mosaic/bar but does NOT reconstruct the hidden
|
||||
detail — it smooths/guesses. For real reconstruction a generative model
|
||||
(DeepMosaics / LADA) is needed; this is the no-weights, no-GPU fallback so the
|
||||
"Расцензурить кадр" flow works end-to-end today.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from ..detection.types import Detection
|
||||
from .base import Restorer
|
||||
from .mask import detections_to_mask
|
||||
|
||||
|
||||
class InpaintRestorer(Restorer):
|
||||
def __init__(self, radius: int = 3, dilate: int = 2, method: str = "telea") -> None:
|
||||
self.radius = radius
|
||||
self.dilate = dilate
|
||||
self.method = method
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return f"InpaintRestorer({self.method})"
|
||||
|
||||
def restore(self, image: np.ndarray, detections: list[Detection]) -> np.ndarray:
|
||||
if not detections:
|
||||
return image.copy()
|
||||
mask = detections_to_mask(image.shape, detections, dilate=self.dilate)
|
||||
flags = cv2.INPAINT_TELEA if self.method == "telea" else cv2.INPAINT_NS
|
||||
return cv2.inpaint(image, mask, self.radius, flags)
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Build a binary mask of the censored regions from detections."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from ..detection.types import Detection
|
||||
|
||||
|
||||
def detections_to_mask(
|
||||
shape: tuple[int, int], detections: list[Detection], dilate: int = 0
|
||||
) -> np.ndarray:
|
||||
"""White (255) over every detected region (polygon if present, else bbox)."""
|
||||
h, w = shape[:2]
|
||||
mask = np.zeros((h, w), np.uint8)
|
||||
for d in detections:
|
||||
if len(d.polygon) >= 3:
|
||||
cv2.fillPoly(mask, [np.array(d.polygon, np.int32)], 255)
|
||||
else:
|
||||
x, y, bw, bh = d.bbox
|
||||
cv2.rectangle(mask, (x, y), (x + bw, y + bh), 255, -1)
|
||||
if dilate > 0:
|
||||
k = np.ones((dilate * 2 + 1, dilate * 2 + 1), np.uint8)
|
||||
mask = cv2.dilate(mask, k)
|
||||
return mask
|
||||
Reference in New Issue
Block a user