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:
Leonid Pershin
2026-06-06 15:44:14 +03:00
parent 33f20fe681
commit ddc8543647
8 changed files with 419 additions and 35 deletions
+40 -10
View File
@@ -38,14 +38,19 @@ a detector, draws the regions, and shows a detailed per-image list of what it fo
Keep this scope sharp:
- It is a **detection + overlay/inspection** tool. It does **not** remove, restore, or
reconstruct censored content.
- It does **not** generate images. There is **no** ControlNet / SDXL / diffusion
pipeline. (`xinsir/controlnet-union-sdxl-1.0` was considered early but rejected — a
generative model, not a detector. Do not reintroduce it.)
- Primary job is **detection + overlay/inspection**. A **restoration** ("расцензурить")
step was added later (user-requested): per-frame, on-demand, behind a `Restorer`
interface. The shipped engine is a cv2 **inpaint baseline** (fills, does NOT truly
reconstruct); a generative engine (DeepMosaics / LADA BasicVSR++) is the intended
real engine but needs weights + a CUDA GPU and is not wired yet.
- Still **no diffusion / ControlNet / SDXL**. (`xinsir/controlnet-union-sdxl-1.0` was
rejected early — a generative *conditioning* model, not a censorship restorer. Don't
reintroduce it.) Restoration, if upgraded, uses a mosaic-removal model (DeepMosaics/
LADA), not a general text-to-image diffusion pipeline.
- It detects **already-censored** regions, not "content that should be censored"
(i.e. not an NSFW classifier).
- It does **not** decode video. No PyAV. Input is image files only.
- Video is only a one-shot frame-extraction convenience (see below); detection and
restoration operate on image folders.
## Target environment
@@ -91,6 +96,11 @@ hvideotool/
├── video/
│ ├── extract.py # extract_frames(): ffmpeg CLI (cv2 fallback) -> JPGs; keyframe/step modes + downscale
│ └── frame.py # Frame dataclass (image BGR, index, pts) — the detector input type
├── restore/ # "un-censor" detected regions (per-frame)
│ ├── base.py # Restorer ABC: restore(image, detections) -> image
│ ├── factory.py # build_restorer(name) -> inpaint (deepmosaics/lada = not wired yet)
│ ├── inpaint.py # InpaintRestorer (cv2) — baseline, fills not reconstructs
│ └── mask.py # detections_to_mask(shape, dets, dilate)
└── detection/
├── base.py # Detector ABC: detect(frame) -> list[Detection]
├── factory.py # build_detector(config) -> classic | yolo | combined
@@ -115,12 +125,32 @@ hvideotool/
"Детектировать все" (whole folder, progress bar). Do NOT re-add auto-detect-on-select.
Results cache in `_results`; the file-list row gets a count suffix when computed.
Switching detector/model clears the cache (`_invalidate_results`).
- **Collections (curation).** "Создать коллекцию…" makes a destination folder
(`_collections_base()` = the opened folder's parent, else `~/HVideoTool/collections`)
and marks it active. The file list is `ExtendedSelection`; "В коллекцию" / Ctrl+M
**moves** (`shutil.move`, not copy) the selected frames there, removing them from the
- **Collections (curation).** A combo in the left pane (next to the file list, since it
acts on the list selection — not on the toolbar, to keep that uncluttered)
(`collection_combo`) picks the active destination; `_refresh_collections()` repopulates it from sibling folders of the
opened folder (`_collections_base()` = the opened folder's parent, else
`~/HVideoTool/collections`) on load/create, so previously-made collections are
reselectable. Items carry the path in itemData; "— не выбрана —" = None,
"Выбрать папку…" = `"__browse__"` sentinel → `_browse_collection()` for an arbitrary
folder (kept in the combo even if outside base). "Создать…" makes a new one and
selects it. The file list is `ExtendedSelection`; "В коллекцию" / Ctrl+M **moves**
(`shutil.move`, not copy) the selected frames there, removing them from
list/`_files`/cache. `_unique_dest` avoids clobbering (`foo.jpg``foo (1).jpg`).
Use case: sort frames into a training/example set while inspecting detections.
- **Restoration ("Расцензурить кадр").** Toolbar action runs `self._restorer` (built via
`build_restorer`) on the current frame's detections (computing them first if needed),
caches the result in `_restored[path]`, and shows it overlay-free. "Показать оригинал/
результат" toggles (`_showing_restored`); "Сохранить результат" writes
`<stem>_restored.jpg` into the active collection (or beside the frame). The baseline
is cv2 inpaint — honest: it fills, doesn't reconstruct. To add a real engine,
implement `core/restore/base.Restorer`, register it in `restore/factory.build_restorer`,
and swap `self._restorer`. `_show` resets `_showing_restored` + `_update_restore_actions`.
- **Navigation bar** under the image (`_build_nav_bar`): prev/next frame (◀ ▶, keys
`,`/`.`), a scrubber `frame_slider` across the whole sequence, a `pos_label`
("row / n"), and jump-to-detection (◀ детекция / детекция ▶, keys `[`/`]`,
`_step_hit` scans `_results` for the next non-empty frame). The slider and file list
are kept in sync via `_update_nav` guarded by `_nav_sync` (avoids signal loops); all
navigation ultimately drives `file_list.setCurrentRow`.
- `image_view.ImageView` draws the image scaled-to-fit plus overlays. Overlay
visibility/threshold are applied at paint time. Selecting a row in the detail table
calls `set_highlight(i)` — that detection is drawn boldly (even below threshold) and