Update core/app.py
Browse files- core/app.py +124 -10
core/app.py
CHANGED
|
@@ -236,6 +236,66 @@ def process_video(
|
|
| 236 |
preview_mask: bool = False,
|
| 237 |
preview_greenscreen: bool = False,
|
| 238 |
) -> Tuple[Optional[str], Optional[str], str]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
if not self.models_loaded or not self.core_processor:
|
| 240 |
return None, None, "Models not loaded. Please click 'Load Models' first."
|
| 241 |
if self.cancel_event.is_set():
|
|
@@ -305,7 +365,11 @@ def _process_single_stage(
|
|
| 305 |
preview_mask: bool,
|
| 306 |
preview_greenscreen: bool,
|
| 307 |
) -> Tuple[Optional[str], Optional[str], str]:
|
| 308 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
ts = int(time.time())
|
| 310 |
out_dir = Path(self.config.output_dir) / "single_stage"
|
| 311 |
out_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -361,7 +425,12 @@ def _process_two_stage(
|
|
| 361 |
) -> Tuple[Optional[str], Optional[str], str]:
|
| 362 |
if self.two_stage_processor is None:
|
| 363 |
return None, None, "Two-stage processor not available"
|
| 364 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
cap = cv2.VideoCapture(video_path)
|
| 366 |
if not cap.isOpened():
|
| 367 |
return None, None, "Could not open input video"
|
|
@@ -502,27 +571,72 @@ def main():
|
|
| 502 |
logger.info("Starting BackgroundFX Pro")
|
| 503 |
logger.info(f"Device: {processor.device_manager.get_optimal_device()}")
|
| 504 |
logger.info(f"Two-stage available: {TWO_STAGE_AVAILABLE}")
|
|
|
|
| 505 |
# 🔹 Quiet model self-check (defaults to async; set SELF_CHECK_MODE=sync to block)
|
| 506 |
if schedule_startup_selfcheck is not None:
|
| 507 |
try:
|
| 508 |
schedule_startup_selfcheck(mode=os.getenv("SELF_CHECK_MODE", "async"))
|
| 509 |
except Exception as e:
|
| 510 |
logger.error(f"Startup self-check skipped: {e}", exc_info=True)
|
|
|
|
| 511 |
# Log model loader type
|
| 512 |
try:
|
| 513 |
from models.loaders.model_loader import ModelLoader
|
| 514 |
logger.info("Using split loader architecture")
|
| 515 |
except Exception:
|
| 516 |
logger.info("Using legacy loader")
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 525 |
finally:
|
| 526 |
processor.cleanup_resources()
|
|
|
|
| 527 |
if __name__ == "__main__":
|
| 528 |
main()
|
|
|
|
| 236 |
preview_mask: bool = False,
|
| 237 |
preview_greenscreen: bool = False,
|
| 238 |
) -> Tuple[Optional[str], Optional[str], str]:
|
| 239 |
+
|
| 240 |
+
# ===== BACKGROUND PATH DEBUG & FIX =====
|
| 241 |
+
logger.info("=" * 60)
|
| 242 |
+
logger.info("BACKGROUND PATH DEBUGGING")
|
| 243 |
+
logger.info(f"background_choice: {background_choice}")
|
| 244 |
+
logger.info(f"custom_background_path type: {type(custom_background_path)}")
|
| 245 |
+
logger.info(f"custom_background_path value: {custom_background_path}")
|
| 246 |
+
|
| 247 |
+
# Fix 1: Handle if Gradio sends a dict
|
| 248 |
+
if isinstance(custom_background_path, dict):
|
| 249 |
+
original = custom_background_path
|
| 250 |
+
custom_background_path = custom_background_path.get('name') or custom_background_path.get('path')
|
| 251 |
+
logger.info(f"Extracted path from dict: {original} -> {custom_background_path}")
|
| 252 |
+
|
| 253 |
+
# Fix 2: Handle PIL Image objects
|
| 254 |
+
try:
|
| 255 |
+
from PIL import Image
|
| 256 |
+
if isinstance(custom_background_path, Image.Image):
|
| 257 |
+
import tempfile
|
| 258 |
+
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
|
| 259 |
+
custom_background_path.save(tmp.name)
|
| 260 |
+
custom_background_path = tmp.name
|
| 261 |
+
logger.info(f"Saved PIL Image to: {custom_background_path}")
|
| 262 |
+
except ImportError:
|
| 263 |
+
pass
|
| 264 |
+
|
| 265 |
+
# Fix 3: Verify file exists when using custom background
|
| 266 |
+
if background_choice == "custom" or custom_background_path:
|
| 267 |
+
if custom_background_path:
|
| 268 |
+
if Path(custom_background_path).exists():
|
| 269 |
+
logger.info(f"✅ Background file exists: {custom_background_path}")
|
| 270 |
+
else:
|
| 271 |
+
logger.warning(f"⚠️ Background file does not exist: {custom_background_path}")
|
| 272 |
+
# Try to find it in Gradio temp directories
|
| 273 |
+
import glob
|
| 274 |
+
patterns = [
|
| 275 |
+
"/tmp/gradio*/**/*.jpg",
|
| 276 |
+
"/tmp/gradio*/**/*.jpeg",
|
| 277 |
+
"/tmp/gradio*/**/*.png",
|
| 278 |
+
"/tmp/**/*.jpg",
|
| 279 |
+
"/tmp/**/*.jpeg",
|
| 280 |
+
"/tmp/**/*.png",
|
| 281 |
+
]
|
| 282 |
+
for pattern in patterns:
|
| 283 |
+
files = glob.glob(pattern, recursive=True)
|
| 284 |
+
if files:
|
| 285 |
+
# Get the most recent file
|
| 286 |
+
newest = max(files, key=os.path.getmtime)
|
| 287 |
+
logger.info(f"Found potential background: {newest}")
|
| 288 |
+
# Only use it if it was created in the last 5 minutes
|
| 289 |
+
if (time.time() - os.path.getmtime(newest)) < 300:
|
| 290 |
+
custom_background_path = newest
|
| 291 |
+
logger.info(f"✅ Using recent temp file: {custom_background_path}")
|
| 292 |
+
break
|
| 293 |
+
else:
|
| 294 |
+
logger.error("❌ Custom background mode but path is None!")
|
| 295 |
+
|
| 296 |
+
logger.info(f"Final custom_background_path: {custom_background_path}")
|
| 297 |
+
logger.info("=" * 60)
|
| 298 |
+
|
| 299 |
if not self.models_loaded or not self.core_processor:
|
| 300 |
return None, None, "Models not loaded. Please click 'Load Models' first."
|
| 301 |
if self.cancel_event.is_set():
|
|
|
|
| 365 |
preview_mask: bool,
|
| 366 |
preview_greenscreen: bool,
|
| 367 |
) -> Tuple[Optional[str], Optional[str], str]:
|
| 368 |
+
|
| 369 |
+
# Additional debug logging for single-stage
|
| 370 |
+
logger.info(f"[Single-stage] background_choice: {background_choice}")
|
| 371 |
+
logger.info(f"[Single-stage] custom_background_path: {custom_background_path}")
|
| 372 |
+
|
| 373 |
ts = int(time.time())
|
| 374 |
out_dir = Path(self.config.output_dir) / "single_stage"
|
| 375 |
out_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
| 425 |
) -> Tuple[Optional[str], Optional[str], str]:
|
| 426 |
if self.two_stage_processor is None:
|
| 427 |
return None, None, "Two-stage processor not available"
|
| 428 |
+
|
| 429 |
+
# Additional debug logging for two-stage
|
| 430 |
+
logger.info(f"[Two-stage] background_choice: {background_choice}")
|
| 431 |
+
logger.info(f"[Two-stage] custom_background_path: {custom_background_path}")
|
| 432 |
+
|
| 433 |
+
import cv2
|
| 434 |
cap = cv2.VideoCapture(video_path)
|
| 435 |
if not cap.isOpened():
|
| 436 |
return None, None, "Could not open input video"
|
|
|
|
| 571 |
logger.info("Starting BackgroundFX Pro")
|
| 572 |
logger.info(f"Device: {processor.device_manager.get_optimal_device()}")
|
| 573 |
logger.info(f"Two-stage available: {TWO_STAGE_AVAILABLE}")
|
| 574 |
+
|
| 575 |
# 🔹 Quiet model self-check (defaults to async; set SELF_CHECK_MODE=sync to block)
|
| 576 |
if schedule_startup_selfcheck is not None:
|
| 577 |
try:
|
| 578 |
schedule_startup_selfcheck(mode=os.getenv("SELF_CHECK_MODE", "async"))
|
| 579 |
except Exception as e:
|
| 580 |
logger.error(f"Startup self-check skipped: {e}", exc_info=True)
|
| 581 |
+
|
| 582 |
# Log model loader type
|
| 583 |
try:
|
| 584 |
from models.loaders.model_loader import ModelLoader
|
| 585 |
logger.info("Using split loader architecture")
|
| 586 |
except Exception:
|
| 587 |
logger.info("Using legacy loader")
|
| 588 |
+
|
| 589 |
+
# FIXED: Move UI import inside main() to avoid circular dependency
|
| 590 |
+
# and add better error handling
|
| 591 |
+
try:
|
| 592 |
+
# Import here to break circular dependency
|
| 593 |
+
from ui import ui_components
|
| 594 |
+
|
| 595 |
+
# Now get the create_interface function
|
| 596 |
+
if hasattr(ui_components, 'create_interface'):
|
| 597 |
+
create_interface = ui_components.create_interface
|
| 598 |
+
else:
|
| 599 |
+
logger.error("create_interface not found in ui_components")
|
| 600 |
+
logger.error(f"Available attributes: {dir(ui_components)}")
|
| 601 |
+
raise ImportError("create_interface function not found")
|
| 602 |
+
|
| 603 |
+
except ImportError as e:
|
| 604 |
+
logger.error(f"Failed to import UI components: {e}")
|
| 605 |
+
import traceback
|
| 606 |
+
traceback.print_exc()
|
| 607 |
+
|
| 608 |
+
# Try alternate import method
|
| 609 |
+
try:
|
| 610 |
+
logger.info("Trying alternate import method...")
|
| 611 |
+
import importlib
|
| 612 |
+
ui_components = importlib.import_module('ui.ui_components')
|
| 613 |
+
create_interface = getattr(ui_components, 'create_interface')
|
| 614 |
+
logger.info("Alternate import successful")
|
| 615 |
+
except Exception as e2:
|
| 616 |
+
logger.error(f"Alternate import also failed: {e2}")
|
| 617 |
+
logger.info("System initialized but UI unavailable. Exiting.")
|
| 618 |
+
return
|
| 619 |
+
|
| 620 |
+
# Create and launch the interface
|
| 621 |
+
try:
|
| 622 |
+
demo = create_interface()
|
| 623 |
+
demo.queue().launch(
|
| 624 |
+
server_name="0.0.0.0",
|
| 625 |
+
server_port=7860,
|
| 626 |
+
show_error=True,
|
| 627 |
+
debug=False,
|
| 628 |
+
)
|
| 629 |
+
except Exception as e:
|
| 630 |
+
logger.error(f"Failed to launch Gradio interface: {e}")
|
| 631 |
+
import traceback
|
| 632 |
+
traceback.print_exc()
|
| 633 |
+
|
| 634 |
+
except Exception as e:
|
| 635 |
+
logger.error(f"Fatal error in main: {e}")
|
| 636 |
+
import traceback
|
| 637 |
+
traceback.print_exc()
|
| 638 |
finally:
|
| 639 |
processor.cleanup_resources()
|
| 640 |
+
|
| 641 |
if __name__ == "__main__":
|
| 642 |
main()
|