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:
@@ -24,6 +24,7 @@ class Detection:
|
||||
bbox: tuple[int, int, int, int] # x, y, w, h
|
||||
polygon: list[tuple[int, int]] = field(default_factory=list) # contour points
|
||||
label: str = "" # model category (models/yolo/<label>); drives colour/grouping
|
||||
model: str = "" # weights file that produced it (the .pt stem)
|
||||
|
||||
@property
|
||||
def display(self) -> str:
|
||||
@@ -37,6 +38,7 @@ class Detection:
|
||||
"bbox": list(self.bbox),
|
||||
"polygon": [list(p) for p in self.polygon],
|
||||
"label": self.label,
|
||||
"model": self.model,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@@ -47,4 +49,5 @@ class Detection:
|
||||
bbox=tuple(data["bbox"]), # type: ignore[arg-type]
|
||||
polygon=[tuple(p) for p in data.get("polygon", [])],
|
||||
label=data.get("label", ""),
|
||||
model=data.get("model", ""),
|
||||
)
|
||||
|
||||
@@ -35,10 +35,14 @@ def _name_to_type(name: str) -> CensorType:
|
||||
|
||||
class YoloDetector(Detector):
|
||||
def __init__(
|
||||
self, model_path: str, config: DetectionConfig | None = None, label: str = ""
|
||||
self, model_path: str, config: DetectionConfig | None = None,
|
||||
label: str = "", model_name: str = "",
|
||||
) -> None:
|
||||
self.cfg = config or DetectionConfig()
|
||||
self._label = label # category (models/yolo/<label>) tagged onto every detection
|
||||
# weights filename (stem) tagged onto every detection, so the UI can show
|
||||
# which specific model predicted it (a category folder may hold several).
|
||||
self._model_name = model_name or os.path.splitext(os.path.basename(model_path))[0]
|
||||
if not os.path.isfile(model_path):
|
||||
raise FileNotFoundError(
|
||||
f"Файл весов не найден: {model_path}\n"
|
||||
@@ -108,6 +112,7 @@ class YoloDetector(Detector):
|
||||
poly = [(int(px), int(py)) for px, py in polygons[i]]
|
||||
ctype = _name_to_type(names.get(int(classes[i]), ""))
|
||||
out.append(Detection(
|
||||
type=ctype, score=float(confs[i]), bbox=bbox, polygon=poly, label=self._label
|
||||
type=ctype, score=float(confs[i]), bbox=bbox, polygon=poly,
|
||||
label=self._label, model=self._model_name,
|
||||
))
|
||||
return out
|
||||
|
||||
@@ -42,6 +42,7 @@ _SETTING_KEYS = (
|
||||
"dm_dir",
|
||||
"dm_model",
|
||||
"dm_gpu",
|
||||
"dm_feed_restored",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -33,7 +33,8 @@ def build_restorer(name: str = "deepmosaics", config: AppConfig | None = None) -
|
||||
from .deepmosaics import DeepMosaicsVideoRestorer
|
||||
|
||||
return DeepMosaicsVideoRestorer(
|
||||
config.dm_dir, config.dm_model, config.dm_gpu
|
||||
config.dm_dir, config.dm_model, config.dm_gpu,
|
||||
feed_restored=getattr(config, "dm_feed_restored", True),
|
||||
)
|
||||
if name == "lada":
|
||||
raise ValueError(
|
||||
|
||||
Reference in New Issue
Block a user