Spaces:
Paused
Paused
MacBook pro
commited on
Commit
·
dc47447
1
Parent(s):
4c5554a
feat(pipeline): add performance stats method, improved CodeFormer import, debug logging
Browse files- swap_pipeline.py +51 -23
swap_pipeline.py
CHANGED
|
@@ -63,6 +63,8 @@ class FaceSwapPipeline:
|
|
| 63 |
self.codeformer = None
|
| 64 |
self.codeformer_fidelity = float(os.getenv('MIRAGE_CODEFORMER_FIDELITY', '0.75'))
|
| 65 |
self.codeformer_loaded = False
|
|
|
|
|
|
|
| 66 |
|
| 67 |
def initialize(self):
|
| 68 |
if self.initialized:
|
|
@@ -157,31 +159,40 @@ class FaceSwapPipeline:
|
|
| 157 |
except Exception:
|
| 158 |
logger.warning('Torch missing; cannot enable CodeFormer')
|
| 159 |
return
|
| 160 |
-
#
|
| 161 |
-
|
|
|
|
| 162 |
try:
|
| 163 |
-
from
|
|
|
|
| 164 |
except Exception:
|
| 165 |
-
need_clone = True
|
| 166 |
-
if need_clone and os.getenv('MIRAGE_CODEFORMER_AUTOCLONE', '1').lower() in ('1','true','yes','on'):
|
| 167 |
-
# Clone into a writable path inside models (repo root may be read-only in some deploy envs)
|
| 168 |
-
repo_dir = os.getenv('MIRAGE_CODEFORMER_REPO_DIR', os.path.join('models', '_codeformer_repo'))
|
| 169 |
-
if self._ensure_repo_clone(repo_dir):
|
| 170 |
-
import sys as _sys
|
| 171 |
-
if repo_dir not in _sys.path:
|
| 172 |
-
_sys.path.append(repo_dir)
|
| 173 |
-
else:
|
| 174 |
-
logger.warning('CodeFormer repo clone failed; enhancement disabled')
|
| 175 |
-
return
|
| 176 |
try:
|
| 177 |
-
from codeformer.archs.codeformer_arch import CodeFormer # type: ignore
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
try:
|
| 186 |
from basicsr.archs.rrdbnet_arch import RRDBNet # noqa: F401
|
| 187 |
except Exception:
|
|
@@ -276,12 +287,20 @@ class FaceSwapPipeline:
|
|
| 276 |
return pcm_bytes
|
| 277 |
|
| 278 |
def process_frame(self, frame: np.ndarray) -> np.ndarray:
|
| 279 |
-
if not self.initialized or self.swapper is None or self.app is None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
return frame
|
| 281 |
t0 = time.time()
|
| 282 |
faces = self.app.get(frame)
|
| 283 |
self._last_faces_cache = faces
|
| 284 |
if not faces:
|
|
|
|
|
|
|
| 285 |
self._record_latency(time.time() - t0)
|
| 286 |
self._stats['swap_faces_last'] = 0
|
| 287 |
return frame
|
|
@@ -298,6 +317,8 @@ class FaceSwapPipeline:
|
|
| 298 |
count += 1
|
| 299 |
except Exception as e:
|
| 300 |
logger.debug(f"Swap failed for face: {e}")
|
|
|
|
|
|
|
| 301 |
# CodeFormer stride / face-region logic
|
| 302 |
apply_cf = (
|
| 303 |
self.codeformer is not None and
|
|
@@ -364,6 +385,13 @@ class FaceSwapPipeline:
|
|
| 364 |
pass
|
| 365 |
return info
|
| 366 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 367 |
# Backwards compatibility for earlier server expecting process_video_frame
|
| 368 |
def process_video_frame(self, frame: np.ndarray, frame_idx: int | None = None) -> np.ndarray:
|
| 369 |
return self.process_frame(frame)
|
|
|
|
| 63 |
self.codeformer = None
|
| 64 |
self.codeformer_fidelity = float(os.getenv('MIRAGE_CODEFORMER_FIDELITY', '0.75'))
|
| 65 |
self.codeformer_loaded = False
|
| 66 |
+
# Debug verbosity for swap decisions
|
| 67 |
+
self.swap_debug = os.getenv('MIRAGE_SWAP_DEBUG', '0').lower() in ('1','true','yes','on')
|
| 68 |
|
| 69 |
def initialize(self):
|
| 70 |
if self.initialized:
|
|
|
|
| 159 |
except Exception:
|
| 160 |
logger.warning('Torch missing; cannot enable CodeFormer')
|
| 161 |
return
|
| 162 |
+
# Direct import path used by upstream project (packaged when installed)
|
| 163 |
+
# Primary expected path: basicsr.archs.* (weights independent). Some forks use codeformer.archs
|
| 164 |
+
CodeFormer = None # type: ignore
|
| 165 |
try:
|
| 166 |
+
from basicsr.archs.codeformer_arch import CodeFormer as _CF # type: ignore
|
| 167 |
+
CodeFormer = _CF # type: ignore
|
| 168 |
except Exception:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
try:
|
| 170 |
+
from codeformer.archs.codeformer_arch import CodeFormer as _CF # type: ignore
|
| 171 |
+
CodeFormer = _CF # type: ignore
|
| 172 |
+
except Exception as e:
|
| 173 |
+
# Attempt repo clone only if autoclone enabled and both imports failed
|
| 174 |
+
if os.getenv('MIRAGE_CODEFORMER_AUTOCLONE', '1').lower() in ('1','true','yes','on'):
|
| 175 |
+
repo_dir = os.getenv('MIRAGE_CODEFORMER_REPO_DIR', os.path.join('models', '_codeformer_repo'))
|
| 176 |
+
if self._ensure_repo_clone(repo_dir):
|
| 177 |
+
import sys as _sys
|
| 178 |
+
if repo_dir not in _sys.path:
|
| 179 |
+
_sys.path.append(repo_dir)
|
| 180 |
+
try:
|
| 181 |
+
from basicsr.archs.codeformer_arch import CodeFormer as _CF2 # type: ignore
|
| 182 |
+
CodeFormer = _CF2 # type: ignore
|
| 183 |
+
except Exception:
|
| 184 |
+
try:
|
| 185 |
+
from codeformer.archs.codeformer_arch import CodeFormer as _CF3 # type: ignore
|
| 186 |
+
CodeFormer = _CF3 # type: ignore
|
| 187 |
+
except Exception as e2:
|
| 188 |
+
logger.warning(f"CodeFormer import failed after clone: {e2}")
|
| 189 |
+
return
|
| 190 |
+
else:
|
| 191 |
+
logger.warning(f"CodeFormer repo clone failed; enhancement disabled ({e})")
|
| 192 |
+
return
|
| 193 |
+
else:
|
| 194 |
+
logger.warning(f"CodeFormer import failed (no autoclone): {e}")
|
| 195 |
+
return
|
| 196 |
try:
|
| 197 |
from basicsr.archs.rrdbnet_arch import RRDBNet # noqa: F401
|
| 198 |
except Exception:
|
|
|
|
| 287 |
return pcm_bytes
|
| 288 |
|
| 289 |
def process_frame(self, frame: np.ndarray) -> np.ndarray:
|
| 290 |
+
if not self.initialized or self.swapper is None or self.app is None:
|
| 291 |
+
if self.swap_debug:
|
| 292 |
+
logger.debug('process_frame: pipeline not fully initialized yet')
|
| 293 |
+
return frame
|
| 294 |
+
if self.source_face is None:
|
| 295 |
+
if self.swap_debug:
|
| 296 |
+
logger.debug('process_frame: no source_face set yet')
|
| 297 |
return frame
|
| 298 |
t0 = time.time()
|
| 299 |
faces = self.app.get(frame)
|
| 300 |
self._last_faces_cache = faces
|
| 301 |
if not faces:
|
| 302 |
+
if self.swap_debug:
|
| 303 |
+
logger.debug('process_frame: no faces detected in incoming frame')
|
| 304 |
self._record_latency(time.time() - t0)
|
| 305 |
self._stats['swap_faces_last'] = 0
|
| 306 |
return frame
|
|
|
|
| 317 |
count += 1
|
| 318 |
except Exception as e:
|
| 319 |
logger.debug(f"Swap failed for face: {e}")
|
| 320 |
+
if self.swap_debug:
|
| 321 |
+
logger.debug(f'process_frame: detected={len(faces)} swapped={count} stride={self.codeformer_frame_stride} apply_cf={count>0 and (self._frame_index % self.codeformer_frame_stride == 0)}')
|
| 322 |
# CodeFormer stride / face-region logic
|
| 323 |
apply_cf = (
|
| 324 |
self.codeformer is not None and
|
|
|
|
| 385 |
pass
|
| 386 |
return info
|
| 387 |
|
| 388 |
+
# Legacy interface used by webrtc_server data channel
|
| 389 |
+
def get_performance_stats(self) -> Dict[str, Any]: # pragma: no cover simple delegate
|
| 390 |
+
stats = self.get_stats()
|
| 391 |
+
# Provide alias field names expected historically (if any)
|
| 392 |
+
stats['frames_processed'] = stats.get('frames')
|
| 393 |
+
return stats
|
| 394 |
+
|
| 395 |
# Backwards compatibility for earlier server expecting process_video_frame
|
| 396 |
def process_video_frame(self, frame: np.ndarray, frame_idx: int | None = None) -> np.ndarray:
|
| 397 |
return self.process_frame(frame)
|