This document walks the pipeline a frame travels through, names every module it touches, and points new contributors at the right file for a given change. Pairs with ROADMAP.md and CHANGELOG.md.
Concrete and up to date as of the v3.28.0 pass. Keep this in sync when modules move.
.
|-- VideoSubtitleRemover.py # Entry point (thin launcher -> gui.app).
|-- gui/
| |-- __init__.py # GUI subpackage.
| |-- app.py # Main Tk shell, shared state, layout, settings.
| |-- processing_controller.py # Queue worker, pause/stop, reports, notify.
| |-- preview_controller.py # Preview pane, A/B compare, region editor.
| |-- quality_controller.py # Quality review, retry, batch-report helpers.
| |-- support_controller.py # Support bundle, model cache, About panels.
| |-- widgets.py # Custom controls: ModernButton, ModernToggle,
| | # ModernSlider, SegmentedPicker, ProgressBar,
| | # DragDropFrame, QueueItemWidget, Tooltip, Toast.
| |-- theme.py # Design tokens, colors, spacing, typography.
| |-- config.py # APP_VERSION, APP_NAME, QueueItem dataclass,
| | # ProcessingConfig (GUI-side), InpaintMode enum,
| | # settings load/save/migrate, preset import.
| `-- utils.py # File helpers, media type checks, formatting.
|-- backend/
| |-- __init__.py # Lazy re-exports SubtitleRemover etc.
| |-- processor.py # Legacy re-export shim + CLI delegation.
| |-- config.py # Backend ProcessingConfig, InpaintMode enum,
| | # coercers, normalize_processing_config.
| |-- detection.py # OCR cascade, selectable engines, confidence,
| | # recognized-text/script filtering.
| |-- tracking.py # Kalman, pHash, karaoke grouping.
| |-- io.py # Capture, ffprobe, intermediate writers,
| | # PrefetchReader.
| |-- cli.py # argparse entry point.
| |-- encoder.py # Output codec probing and HW encoder selection.
| |-- remux.py # Soft-subtitle strip/keep remux paths.
| |-- quality.py # PSNR/SSIM/VMAF helpers.
| |-- quality_gate.py # Graduated quality gate with remediation ladder.
| |-- batch_report.py # JSON + Markdown batch summary output.
| |-- inpainters/ # Built-in STTN/LaMa/ProPainter/AUTO paths.
| | |-- __init__.py # BaseInpainter, mode routing.
| | |-- sttn.py # TBE (Temporal Background Exposure).
| | |-- lama.py # ONNX > OpenCV 5 DNN > PyTorch opt-in > cv2.
| | |-- propainter.py # TBE + LaMa refinement hybrid.
| | |-- auto.py # Per-scene STTN/ProPainter motion routing.
| | `-- _common.py # Feathering, edge-ring color match.
| |-- inpainters_onnx.py # ONNX Runtime inpaint session helpers.
| |-- inpainters_diffusion.py # Opt-in diffusion adapter scaffolds.
| |-- inpainter_registry.py # In-process inpainter discovery registry.
| |-- presets.py # Shared preset library (GUI + CLI).
| |-- model_hashes.py # Vendored SHA-256 hashes + verifier.
| |-- adapter_manifest.py # Optional model provenance and hash policy.
| |-- onnx_model_info.py # ONNX opset audit and DirectML compat check.
| |-- remote_model_policy.py # Gate for trust_remote_code / torch.hub.
| |-- model_downloads.py # First-run model download guidance.
| |-- language_support.py # GUI picker scope + OCR engine language facts.
| |-- paddle_compat.py # PaddleOCR 2.x/3.x API compatibility layer.
| |-- hdr.py # Color metadata preservation.
| |-- decode_accel.py # Hardware decode hints (D3D11/VAAPI/MFX).
| |-- preprocess.py # Deinterlacing, keyframe enumeration.
| |-- post_restore.py # Post-inpaint temporal smoothing.
| |-- whisper_fallback.py # Whisper-based timing for OCR-empty speech.
| |-- karaoke_flow.py # Karaoke optical-flow grouping helper.
| |-- proxy_workflow.py # Proxy-encode workflow for large files.
| |-- nle_sidecar.py # EDL/FCPXML sidecar export.
| |-- tensorrt_compile.py # Optional TensorRT engine compilation.
| |-- cache_inventory.py # --cache-info / --cache-clean.
| |-- update_check.py # Startup version check (opt-in).
| |-- security_checks.py # Runtime safety checks.
| |-- crash_reporter.py # Opt-in GlitchTip reporter (path-scrubbed).
| |-- support_bundle.py # Redacted diagnostics zip export.
| |-- dependency_caps.py # Dependency version ceiling enforcement.
| |-- release_verification.py # Local PyInstaller release evidence writer.
| |-- ocr_vlm.py # Optional VLM detectors (Florence-2, Qwen2.5).
| |-- segmentation.py # Optional SAM 2 / CoTracker adapters.
| |-- i18n.py # Localisation scaffold.
| |-- a11y.py # Accessibility scaffold.
| `-- vapoursynth_bridge.py # VapourSynth bridge scaffold.
|-- tests/
| |-- test_hardening.py # Core regression + fuzz suite.
| |-- test_detection_pipeline.py # OCR cascade unit tests.
| |-- test_tracking_pipeline.py # Kalman/pHash tracking tests.
| |-- test_io_pipeline.py # Capture, intermediate, prefetch tests.
| |-- test_gui_smoke.py # Off-screen GUI construction smoke test.
| |-- test_gui_*.py # GUI widget / state / feedback tests.
| |-- test_release_workflow.py # Local release evidence validation.
| |-- test_support_bundle.py # Support bundle export tests.
| |-- test_preset_schema.py # Preset round-trip validation.
| |-- test_reference_clips.py # Reference-clip quality baselines.
| `-- test_*.py # Additional focused test modules.
|-- .github/
| `-- ISSUE_TEMPLATE/ # Bug report + feature request forms.
|-- docs/
| |-- architecture.md # This file.
| |-- edge_case_corpus.md # Community regression-corpus guide.
| `-- archive/ # Retired audits and completed checklists.
|-- setup.py # First-run venv bootstrap.
|-- scripts/setup_splash.py # Dependency-free first-run progress splash.
|-- Run_VSR_Pro.bat # Windows launcher.
|-- Run_VSR_Pro_Debug.bat # Windows launcher with visible console.
|-- Run_VSR_Pro.ps1 # PowerShell launcher (generated by setup).
|-- build_exe.bat # Local PyInstaller build + evidence script.
`-- requirements.txt # Pinned + advisory deps.
gui/app.pyowns the Tk shell, shared state, layout, settings variables, queue model, and publicVideoSubtitleRemoverAppsurface. The default shell is a command-first workbench: one compact command strip sits above the preview/inspector split, and the persistent queue is rendered as a dense table below it. Advanced controls remain progressively disclosed in the inspector rather than competing with the primary workflow.gui/processing_controller.pyowns queue processing, pause/stop orchestration, per-item backend dispatch, progress/taskbar updates, report preparation, and completion notifications.gui/preview_controller.pyowns preview placeholders, live frames, mask review, A/B compare, test-cleanup previews, preview zoom, and the inline region editor.gui/quality_controller.pyowns batch-summary dialogs, source-aware quality warnings, quality-review worklists, retry-with-suggested-settings, and batch report file opening/writing.gui/support_controller.pyowns the log panel, support bundle export, model-cache import/export, backend-status panel, and About dialog.gui/widgets.pycontains all custom controls:ModernButton,ModernToggle,ModernSlider,SegmentedPicker,ModernProgressBar,DragDropFrame,QueueItemWidget,Toast,Tooltip, and themed utility functions.gui/config.pyis the single source of truth forAPP_VERSION, the GUIProcessingConfigdataclass,QueueItem, settings load/save/migrate, and preset import/export.backend/config.pyowns the backendProcessingConfig,InpaintModeenum, coercers, andnormalize_processing_config. Inpainters importbackend.configdirectly.backend/processor.pypreserves the legacy public import surface and delegatespython -m backend.processortobackend.cli.main.backend/detection.py,backend/tracking.py,backend/io.py,backend/quality.py, andbackend/inpainters/own the focused pipeline pieces.backend/encoder.pyprobes hardware encoders and selects the output codec (H.264 / H.265 / AV1 / VVC).backend/presets.pyholds the one place a preset definition is allowed to live.backend/model_hashes.pyowns vendored weight hashes and the chunked SHA-256 verifier.backend/language_support.pyowns the distinction between the GUI's selectable OCR language codes and broader OCR engine language capacity reported in support/backend status.
- Ingest.
gui.app.VideoSubtitleRemoverApp._on_files_dropped->_add_to_queuebuildsQueueItementries, each carrying its ownProcessingConfigsnapshot. Queue capped at 500. - Per-item dispatch.
_process_queuewalks the queue;_process_itemtranslates the GUIProcessingConfigto the backendProcessingConfigand instantiates aSubtitleRemover(or reuses a cached one when mode/device/lang match). - Backend constructor.
backend.processor.SubtitleRemover.__init__:- Normalises the config via
normalize_processing_config. - Builds the OCR
SubtitleDetector(cascade resolution). - Picks the inpainter (
STTNInpainter/LAMAInpainter/ProPainterInpainter/AutoInpainter). - Probes the matching HW encoder family for
output_codec. - Optional NVML free-VRAM probe scales
sttn_max_load_num.
- Normalises the config via
- Optional preprocessing.
process_video:- ffprobe
idet->ffmpeg yadifdeinterlace when auto-detected. - ffprobe keyframe enumeration when
keyframe_detection.
- ffprobe
- Decode.
_open_captureeither opens acv2.VideoCapture(with optionaldecode_hw_accel) or a_FrameSequenceCapturefor an image-directory input. Whenprefetch_decodeis on, the cap is wrapped in a_PrefetchReaderdaemon worker that feeds a bounded queue. - Per-frame detect. Inside the main loop:
pHashskip + keyframe gating short-circuit when content is unchanged.SubtitleDetector.detect(frame)calls the active engine (RapidOCR / PaddleOCR / Surya / EasyOCR / OpenCV)._group_horizontal_linefuses karaoke syllables.SubtitleTracker.updatesmooths via Kalman.categorizefilters chyron vs subtitle when eitherremove_chyrons/remove_subtitlesis off._create_maskproduces the binary mask (with dilation)._expand_mask_by_colorextends to dominant-colour pixels._accumulate_quality_bboxwidens the union-mask bbox used by the ROI quality metric.
- Per-batch inpaint. The current batch of
(frame, mask)pairs is passed to the inpainter chosen above:STTNInpainter:_temporal_background_exposereconstructs the true background from temporally-exposed neighbours.LAMAInpainter: ONNX Runtime > OpenCV 5 DNN > PyTorch (simple-lama-inpainting, only whenVSR_ENABLE_PYTORCH_LAMA=1)cv2.inpaint four-tier chain.
ProPainterInpainter: TBE with a higher coverage bar + LaMa residual blend (MIT-licensed hybrid, not the ICCV 2023 model).AutoInpainter: per-batch routing on the exposure score; idle-LaMa is unloaded afterLAMA_IDLE_UNLOAD_AFTERTBE batches. All paths terminate inapply_finishing(edge-ring colour match then feather blend).
- Intermediate write.
_LosslessIntermediateWriterpipes raw BGR frames throughffmpeg -c:v ffv1so the final encode is the only lossy step. Falls back to legacymp4vwhen ffmpeg is absent. - Mux + finalise.
_merge_audiore-encodes the FFV1 temp into the user-visible H.264 / H.265 / AV1 / VVC (H.266) output (HW encoder when available, software fallback peroutput_codec). Audio path honours:- Time-range trim.
- Multi-track passthrough (
-map 1:a?). - Per-stream loudness normalisation (
-filter_complexbranch). - Adaptive
_ffmpeg_subprocess_timeoutscaled to source duration.
- Quality report. When
quality_reportis on,_compute_quality_reportsamples N frames from input + output, computes both whole-frame and ROI-cropped PSNR/SSIM (the ROI is the union mask bbox). Optional_write_quality_sheetrenders the side-by-side PNG. The quality gate ladder escalates through increase-dilation, temporal-smooth, alternate-inpainter, and manual-review. - Batch report.
backend/batch_report.pywritesvsr-batch-summary.jsonandvsr-batch-summary.mdwith per-item status, codec/duration data, quality gate results, and remediation suggestions. - Progress, preview, cancel. During every batch:
on_progress(progress, message)ticks the GUI progress bar and Windows taskbar.on_preview_frame(frame, idx, total)marshals an inpainted frame to the Tk preview pane.cancel_event(global) and the per-itemcancel_requestedflag each raiseInterruptedErrorfrom the progress callback so the batch can stop cleanly.
[settings.json]
| load_settings + _migrate_settings (schema backfill)
v
[GUI ProcessingConfig] (single source of truth in gui/config.py)
| per QueueItem snapshot via to_dict / from_dict
v
[QueueItem.config] (immutable from this point unless re-snapshotted)
| _process_item builds the BackendConfig and passes it down
v
[backend ProcessingConfig] (backend/config.py)
| normalize_processing_config (idempotent, runs on hot-swap)
v
[runtime: SubtitleRemover.config]
- Persistence is dataclass-driven via
dataclasses.fields(self)so a new field lands in settings.json automatically. - The GUI
_sync_config_from_uiis the one place a tk variable maps to a config field; every new toggle adds ahasattrguard here. - Hot-swap of a cached remover re-runs
normalize_processing_config(backend_config)to defang any NaN/inf or out-of-range per-item override.
For a new ProcessingConfig field:
- Declare the dataclass field with a backend-default value in
both
gui/config.py:ProcessingConfigandbackend/config.py:ProcessingConfig. - Add a coercion entry to
normalize_processing_config(backend) andProcessingConfig.normalized()(GUI) with safe bounds. - Pass the field through
_process_item->BackendConfig(...). - Surface it in the GUI: add a tk variable + an Advanced card widget,
then sync it in
_sync_config_from_ui. - Surface it on the CLI: add
parser.add_argument(...)and map it in theconfig = ProcessingConfig(...)block. - Add a regression test that round-trips the field through to_dict + from_dict.
- Bump
VSR_SETTINGS_FORMATonly when the new field's semantics require a migration -- a backend-default new key does not.
For a new inpainter:
- Subclass
BaseInpainterwith aninpaint(frames, masks)method. - Add the mode to
InpaintMode(both GUI and backend enums). - Add a branch to
SubtitleRemover._create_inpainter. - Add the GUI label to
InpaintModeandmode_mapin_process_item. - If the inpainter loads weights at runtime, register the
vendored SHA-256 in
backend/model_hashes.py:KNOWN_WEIGHT_HASHESand callverify_weight_filefrom the loader.
For a new OCR detector:
- Add a probe + lazy-load block in
SubtitleDetector._load_model(respect the cascade priority). - Add a
_detect_<engine>method that returns a list of(x1, y1, x2, y2)tuples. - Register the engine name in
detect_ai_engines()for the About dialog. - If GPL-licensed, gate behind
VSR_ALLOW_GPLlike Surya.
Microsoft has placed DirectML in maintenance mode (explicit README banner on github.com/microsoft/DirectML as of 2026). New ONNX Runtime GPU development for AMD/Intel targets is moving to Windows ML.
Current VSR state:
- AMD/Intel GPU inference uses
onnxruntime-directml==1.24.4(latest published, March 2026). It receives security patches but no new features. onnxruntime-windowsml1.27.1 is available on PyPI (Python 3.11-3.14) and provides automatic execution-provider selection.--audit-windows-mlprobes the Windows ML Python path.backend/device_provider.py:windowsml_status()reports whetheronnxruntime-windowsmlis installed (surfaced in the support bundle).
Migration prerequisites:
- Confirm
onnxruntime-windowsmlprovides equivalent EP selection for the OCR and inpaint ONNX models on AMD/Intel GPUs. - Benchmark latency versus the current DirectML path.
- Update
dependency_profiles/directml.txtto pinonnxruntime-windowsmlinstead ofonnxruntime-directml. - Update
setup.pyto install the new package on AMD/Intel hardware.
No urgent action is needed: DirectML continues to receive security
patches and functions correctly. Track onnxruntime-windowsml
releases and confirm inference parity before switching the default.
process_videois monolithic (~250 lines). Splitting it into detect / inpaint / mux phases is on the roadmap but every existing call site assumes the current state machine, so the split needs careful test coverage first.- The cached-remover reuse in
_process_itemsaves model-load time across batch items but means a config change that needs a different detector engine triggers a full reload (mode + device + lang form the cache key). _PrefetchReaderrequires strict ownership: once it wraps a cap, the main thread cannot touch the underlying object directly. Cleanup goes throughreader.release()so a mid-batch crash never leaks the worker thread.- The FFV1 intermediate (
_LosslessIntermediateWriter) needs ffmpeg on PATH; falls back tomp4vautomatically. The fallback reverts to v3.12 behaviour (lossy intermediate).