MogensR commited on
Commit
fb41e40
·
1 Parent(s): c01f936

Update processing/video/video_processor.py

Browse files
Files changed (1) hide show
  1. processing/video/video_processor.py +105 -18
processing/video/video_processor.py CHANGED
@@ -1,9 +1,101 @@
1
- # ... (all your imports and CoreVideoProcessor class header/attributes as above)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  class CoreVideoProcessor:
4
  """
5
  Minimal, safe implementation used by core/app.py.
6
- ...
 
7
  """
8
 
9
  def __init__(self, config: Optional[ProcessorConfig] = None, models: Optional[Any] = None):
@@ -13,7 +105,6 @@ def __init__(self, config: Optional[ProcessorConfig] = None, models: Optional[An
13
  if self.models is None:
14
  self.log.warning("CoreVideoProcessor initialized without a models provider; will use fallbacks.")
15
  self._ffmpeg = shutil.which("ffmpeg")
16
- # (rest as before...)
17
 
18
  # -------- Back-compat safe config flags (do not require attrs on user config)
19
  self._use_windowed = _env_bool(
@@ -40,7 +131,6 @@ def prepare_background(self, background_choice: str, custom_background_path: Opt
40
  If a valid custom background path is given, loads and resizes it. Otherwise, uses a preset.
41
  Returns: np.ndarray RGB (H, W, 3) uint8
42
  """
43
- import cv2
44
  from utils.cv_processing import create_professional_background
45
 
46
  if custom_background_path:
@@ -58,8 +148,6 @@ def prepare_background(self, background_choice: str, custom_background_path: Opt
58
  # fallback to preset
59
  return create_professional_background(background_choice, width, height)
60
 
61
- # (rest of class unchanged...)
62
-
63
  # ---------- mask post-processing (stability + crispness) ----------
64
  def _iou(self, a: np.ndarray, b: np.ndarray, thr: float = 0.5) -> float:
65
  a_bin = (a >= thr).astype(np.uint8)
@@ -189,7 +277,7 @@ def _prepare_background_from_config(
189
  img_bgr = cv2.resize(img_bgr, (width, height), interpolation=cv2.INTER_LANCZOS4)
190
  return cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
191
 
192
- if bg_config and isinstance(bg_config.get("gradient"), dict):
193
  try:
194
  return _create_gradient_background_local(bg_config["gradient"], width, height)
195
  except Exception as e:
@@ -295,13 +383,13 @@ def process_video(
295
 
296
  self._prev_mask = None
297
 
298
- ffmpeg_pipe: _FFmpegPipe | None = None
299
  writer: cv2.VideoWriter | None = None
300
  ffmpeg_failed_reason = None
301
 
302
- if getattr(self.config, "use_nvenc", True) and shutil.which("ffmpeg"):
303
  try:
304
- ffmpeg_pipe = _FFmpegPipe(width, height, float(fps_out), output_path, self.config, log=self.log)
305
  except Exception as e:
306
  ffmpeg_failed_reason = str(e)
307
  self.log.warning("FFmpeg NVENC pipeline unavailable. Falling back to OpenCV. Reason: %s", e)
@@ -347,11 +435,11 @@ def process_video(
347
 
348
  if ffmpeg_pipe is not None:
349
  try:
350
- ffmpeg_pipe.write(out_bgr)
351
  except Exception as e:
352
  self.log.warning("Switching to OpenCV writer after FFmpeg error at frame %d: %s", frame_count, e)
353
  try:
354
- ffmpeg_pipe.close()
355
  except Exception:
356
  pass
357
  ffmpeg_pipe = None
@@ -443,11 +531,11 @@ def process_video(
443
 
444
  if ffmpeg_pipe is not None:
445
  try:
446
- ffmpeg_pipe.write(out_bgr)
447
  except Exception as e:
448
  self.log.warning("Switching to OpenCV writer after FFmpeg error at frame %d: %s", frame_count, e)
449
  try:
450
- ffmpeg_pipe.close()
451
  except Exception:
452
  pass
453
  ffmpeg_pipe = None
@@ -481,10 +569,10 @@ def process_video(
481
 
482
  if ffmpeg_pipe is not None:
483
  try:
484
- ffmpeg_pipe.write(np.ascontiguousarray(out_bgr_fb))
485
  except Exception:
486
  try:
487
- ffmpeg_pipe.close()
488
  except Exception:
489
  pass
490
  ffmpeg_pipe = None
@@ -520,7 +608,7 @@ def process_video(
520
  writer.release()
521
  if ffmpeg_pipe is not None:
522
  try:
523
- ffmpeg_pipe.close()
524
  except Exception:
525
  pass
526
 
@@ -535,4 +623,3 @@ def process_video(
535
  "fps_out": float(fps_out),
536
  "output_path": output_path,
537
  }
538
-
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Core Video Processor for BackgroundFX Pro
5
+
6
+ - Minimal, safe implementation used by core/app.py
7
+ - Works with split/legacy loaders
8
+ - Keeps exact behavior you shared; only fixes typing imports + integrates
9
+ prepare_background() and related helpers.
10
+
11
+ NOTE:
12
+ - Requires utils.cv_processing helpers already present in your project:
13
+ segment_person_hq, refine_mask_hq, replace_background_hq,
14
+ create_professional_background, PROFESSIONAL_BACKGROUNDS,
15
+ _create_gradient_background_local, validate_video_file
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import os
21
+ import time
22
+ import shutil
23
+ import logging
24
+ import threading
25
+ from typing import Optional, Any, Dict, Callable, Tuple, List
26
+
27
+ import numpy as np
28
+ import cv2
29
+
30
+ # ---------------------------------------------------------------------
31
+ # Project logger (non-fatal fallback to std logging)
32
+ # ---------------------------------------------------------------------
33
+ try:
34
+ from utils.logger import get_logger
35
+ _log = get_logger("processing.video.video_processor")
36
+ except Exception:
37
+ logging.basicConfig(level=logging.INFO)
38
+ _log = logging.getLogger("processing.video.video_processor")
39
+
40
+ # ---------------------------------------------------------------------
41
+ # Config type (import if available; otherwise annotations are postponed)
42
+ # ---------------------------------------------------------------------
43
+ try:
44
+ from config.processor_config import ProcessorConfig # your project config
45
+ except Exception: # keep runtime happy if only used for typing
46
+ ProcessorConfig = Any # type: ignore
47
+
48
+ # ---------------------------------------------------------------------
49
+ # Small env helpers (use project ones if you have them)
50
+ # ---------------------------------------------------------------------
51
+ try:
52
+ from utils.system.env_utils import env_bool as _env_bool # type: ignore
53
+ from utils.system.env_utils import env_int as _env_int # type: ignore
54
+ except Exception:
55
+ def _env_bool(name: str, default: bool = False) -> bool:
56
+ v = os.environ.get(name)
57
+ if v is None:
58
+ return bool(default)
59
+ return str(v).strip().lower() in ("1", "true", "yes", "y", "on")
60
+
61
+ def _env_int(name: str, default: int = 0) -> int:
62
+ try:
63
+ return int(os.environ.get(name, default))
64
+ except Exception:
65
+ return int(default)
66
+
67
+ # ---------------------------------------------------------------------
68
+ # CV helpers from your utils module
69
+ # ---------------------------------------------------------------------
70
+ from utils.cv_processing import (
71
+ segment_person_hq,
72
+ refine_mask_hq,
73
+ replace_background_hq,
74
+ create_professional_background,
75
+ PROFESSIONAL_BACKGROUNDS,
76
+ validate_video_file,
77
+ )
78
+
79
+ # Optional local gradient helper (present in some layouts)
80
+ try:
81
+ from utils.cv_processing import _create_gradient_background_local # type: ignore
82
+ except Exception:
83
+ _create_gradient_background_local = None # type: ignore
84
+
85
+ # ---------------------------------------------------------------------
86
+ # Optional FFmpeg pipe; code falls back to OpenCV if unavailable
87
+ # ---------------------------------------------------------------------
88
+ try:
89
+ from utils.video.ffmpeg_pipe import FFmpegPipe as _FFmpegPipe # type: ignore
90
+ except Exception:
91
+ _FFmpegPipe = None # type: ignore
92
+
93
 
94
  class CoreVideoProcessor:
95
  """
96
  Minimal, safe implementation used by core/app.py.
97
+ Orchestrates SAM2 → MatAnyone refinement → background compositing,
98
+ with robust fallbacks (OpenCV writer when FFmpeg/NVENC unavailable).
99
  """
100
 
101
  def __init__(self, config: Optional[ProcessorConfig] = None, models: Optional[Any] = None):
 
105
  if self.models is None:
106
  self.log.warning("CoreVideoProcessor initialized without a models provider; will use fallbacks.")
107
  self._ffmpeg = shutil.which("ffmpeg")
 
108
 
109
  # -------- Back-compat safe config flags (do not require attrs on user config)
110
  self._use_windowed = _env_bool(
 
131
  If a valid custom background path is given, loads and resizes it. Otherwise, uses a preset.
132
  Returns: np.ndarray RGB (H, W, 3) uint8
133
  """
 
134
  from utils.cv_processing import create_professional_background
135
 
136
  if custom_background_path:
 
148
  # fallback to preset
149
  return create_professional_background(background_choice, width, height)
150
 
 
 
151
  # ---------- mask post-processing (stability + crispness) ----------
152
  def _iou(self, a: np.ndarray, b: np.ndarray, thr: float = 0.5) -> float:
153
  a_bin = (a >= thr).astype(np.uint8)
 
277
  img_bgr = cv2.resize(img_bgr, (width, height), interpolation=cv2.INTER_LANCZOS4)
278
  return cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
279
 
280
+ if bg_config and isinstance(bg_config.get("gradient"), dict) and _create_gradient_background_local:
281
  try:
282
  return _create_gradient_background_local(bg_config["gradient"], width, height)
283
  except Exception as e:
 
383
 
384
  self._prev_mask = None
385
 
386
+ ffmpeg_pipe: _FFmpegPipe | None = None # type: ignore
387
  writer: cv2.VideoWriter | None = None
388
  ffmpeg_failed_reason = None
389
 
390
+ if getattr(self.config, "use_nvenc", True) and shutil.which("ffmpeg") and _FFmpegPipe is not None:
391
  try:
392
+ ffmpeg_pipe = _FFmpegPipe(width, height, float(fps_out), output_path, self.config, log=self.log) # type: ignore
393
  except Exception as e:
394
  ffmpeg_failed_reason = str(e)
395
  self.log.warning("FFmpeg NVENC pipeline unavailable. Falling back to OpenCV. Reason: %s", e)
 
435
 
436
  if ffmpeg_pipe is not None:
437
  try:
438
+ ffmpeg_pipe.write(out_bgr) # type: ignore[attr-defined]
439
  except Exception as e:
440
  self.log.warning("Switching to OpenCV writer after FFmpeg error at frame %d: %s", frame_count, e)
441
  try:
442
+ ffmpeg_pipe.close() # type: ignore[attr-defined]
443
  except Exception:
444
  pass
445
  ffmpeg_pipe = None
 
531
 
532
  if ffmpeg_pipe is not None:
533
  try:
534
+ ffmpeg_pipe.write(out_bgr) # type: ignore[attr-defined]
535
  except Exception as e:
536
  self.log.warning("Switching to OpenCV writer after FFmpeg error at frame %d: %s", frame_count, e)
537
  try:
538
+ ffmpeg_pipe.close() # type: ignore[attr-defined]
539
  except Exception:
540
  pass
541
  ffmpeg_pipe = None
 
569
 
570
  if ffmpeg_pipe is not None:
571
  try:
572
+ ffmpeg_pipe.write(np.ascontiguousarray(out_bgr_fb)) # type: ignore[attr-defined]
573
  except Exception:
574
  try:
575
+ ffmpeg_pipe.close() # type: ignore[attr-defined]
576
  except Exception:
577
  pass
578
  ffmpeg_pipe = None
 
608
  writer.release()
609
  if ffmpeg_pipe is not None:
610
  try:
611
+ ffmpeg_pipe.close() # type: ignore[attr-defined]
612
  except Exception:
613
  pass
614
 
 
623
  "fps_out": float(fps_out),
624
  "output_path": output_path,
625
  }