Update streamlit_app.py
Browse files- streamlit_app.py +67 -40
streamlit_app.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
| 1 |
# streamlit_ui.py
|
|
|
|
|
|
|
| 2 |
import streamlit as st
|
| 3 |
import os
|
| 4 |
import sys
|
|
@@ -10,15 +12,24 @@
|
|
| 10 |
from PIL import Image
|
| 11 |
import logging
|
| 12 |
import io
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
-
#
|
| 15 |
sys.path.append(str(Path(__file__).parent.absolute()))
|
| 16 |
|
| 17 |
-
#
|
| 18 |
-
logging.basicConfig(level=logging.INFO)
|
| 19 |
logger = logging.getLogger(__name__)
|
| 20 |
|
| 21 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
st.set_page_config(
|
| 23 |
page_title="π¬ Advanced Video Background Replacer",
|
| 24 |
page_icon="π₯",
|
|
@@ -26,7 +37,7 @@
|
|
| 26 |
initial_sidebar_state="expanded"
|
| 27 |
)
|
| 28 |
|
| 29 |
-
# Custom CSS
|
| 30 |
st.markdown("""
|
| 31 |
<style>
|
| 32 |
.main .block-container {
|
|
@@ -73,6 +84,7 @@
|
|
| 73 |
</style>
|
| 74 |
""", unsafe_allow_html=True)
|
| 75 |
|
|
|
|
| 76 |
def initialize_session_state():
|
| 77 |
"""Initialize all session state variables"""
|
| 78 |
if 'uploaded_video' not in st.session_state:
|
|
@@ -90,16 +102,18 @@ def initialize_session_state():
|
|
| 90 |
if 'progress_text' not in st.session_state:
|
| 91 |
st.session_state.progress_text = "Ready"
|
| 92 |
|
|
|
|
| 93 |
def handle_video_upload():
|
| 94 |
"""Handle video file upload"""
|
| 95 |
uploaded = st.file_uploader(
|
| 96 |
-
"πΉ Upload Video",
|
| 97 |
type=["mp4", "mov", "avi"],
|
| 98 |
key="video_uploader"
|
| 99 |
)
|
| 100 |
if uploaded is not None:
|
| 101 |
st.session_state.uploaded_video = uploaded
|
| 102 |
|
|
|
|
| 103 |
def show_video_preview():
|
| 104 |
"""Show video preview in the UI"""
|
| 105 |
st.markdown("### Video Preview")
|
|
@@ -108,6 +122,7 @@ def show_video_preview():
|
|
| 108 |
st.video(video_bytes)
|
| 109 |
st.session_state.uploaded_video.seek(0)
|
| 110 |
|
|
|
|
| 111 |
def handle_background_selection():
|
| 112 |
"""Handle background selection UI"""
|
| 113 |
st.markdown("### Background Options")
|
|
@@ -127,14 +142,14 @@ def handle_background_selection():
|
|
| 127 |
if bg_image is not None:
|
| 128 |
st.session_state.bg_image = Image.open(bg_image)
|
| 129 |
st.image(
|
| 130 |
-
st.session_state.bg_image,
|
| 131 |
-
caption="Selected Background",
|
| 132 |
use_container_width=True
|
| 133 |
)
|
| 134 |
|
| 135 |
elif bg_type == "Color":
|
| 136 |
st.session_state.bg_color = st.color_picker(
|
| 137 |
-
"π¨ Choose Background Color",
|
| 138 |
st.session_state.bg_color
|
| 139 |
)
|
| 140 |
color_rgb = tuple(int(st.session_state.bg_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
|
|
@@ -144,6 +159,8 @@ def handle_background_selection():
|
|
| 144 |
|
| 145 |
return bg_type.lower()
|
| 146 |
|
|
|
|
|
|
|
| 147 |
def process_video(input_file, background, bg_type="image"):
|
| 148 |
"""
|
| 149 |
Process video with the selected background using SAM2 and MatAnyone pipeline.
|
|
@@ -156,8 +173,12 @@ def process_video(input_file, background, bg_type="image"):
|
|
| 156 |
|
| 157 |
# Save the uploaded video to a temporary file
|
| 158 |
input_path = str(temp_dir / "input.mp4")
|
|
|
|
| 159 |
with open(input_path, "wb") as f:
|
| 160 |
-
f.write(input_file.getvalue())
|
|
|
|
|
|
|
|
|
|
| 161 |
|
| 162 |
# Prepare background
|
| 163 |
bg_path = None
|
|
@@ -173,43 +194,53 @@ def process_video(input_file, background, bg_type="image"):
|
|
| 173 |
bg_path = str(temp_dir / "background.jpg")
|
| 174 |
cv2.imwrite(bg_path, np.ones((100, 100, 3), dtype=np.uint8) * color_rgb[::-1])
|
| 175 |
|
| 176 |
-
# Set up progress
|
| 177 |
-
|
| 178 |
-
|
| 179 |
|
| 180 |
def progress_callback(progress, message):
|
| 181 |
progress = max(0, min(1, float(progress)))
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
|
|
|
|
|
|
| 186 |
|
| 187 |
# Process the video
|
| 188 |
output_path = str(temp_dir / "output.mp4")
|
| 189 |
-
|
| 190 |
-
# Mock click points (center of the frame)
|
| 191 |
click_points = [[0.5, 0.5]]
|
| 192 |
-
|
| 193 |
-
# Import the pipeline processor
|
| 194 |
from pipeline.integrated_pipeline import TwoStageProcessor
|
| 195 |
|
| 196 |
-
#
|
| 197 |
-
|
|
|
|
|
|
|
|
|
|
| 198 |
|
| 199 |
-
#
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 208 |
|
| 209 |
if not success:
|
| 210 |
raise RuntimeError("Video processing failed")
|
| 211 |
|
| 212 |
-
# Return the path to the processed video
|
| 213 |
return output_path
|
| 214 |
|
| 215 |
except Exception as e:
|
|
@@ -217,6 +248,7 @@ def progress_callback(progress, message):
|
|
| 217 |
st.error(f"An error occurred during processing: {str(e)}")
|
| 218 |
return None
|
| 219 |
|
|
|
|
| 220 |
def main():
|
| 221 |
st.title("π¬ Advanced Video Background Replacer")
|
| 222 |
st.markdown("---")
|
|
@@ -235,7 +267,6 @@ def main():
|
|
| 235 |
with col2:
|
| 236 |
st.header("2. Background Settings")
|
| 237 |
bg_type = handle_background_selection()
|
| 238 |
-
|
| 239 |
st.header("3. Process & Download")
|
| 240 |
# ------- FORM WRAP START --------
|
| 241 |
with st.form("process_form"):
|
|
@@ -243,7 +274,6 @@ def main():
|
|
| 243 |
"π Process Video",
|
| 244 |
disabled=not st.session_state.uploaded_video or st.session_state.processing
|
| 245 |
)
|
| 246 |
-
|
| 247 |
process_status = st.empty()
|
| 248 |
if submitted and not st.session_state.processing:
|
| 249 |
st.session_state.processing = True
|
|
@@ -255,27 +285,23 @@ def main():
|
|
| 255 |
background = st.session_state.bg_image
|
| 256 |
elif bg_type == "color" and 'bg_color' in st.session_state:
|
| 257 |
background = st.session_state.bg_color
|
| 258 |
-
|
| 259 |
# Process the video
|
| 260 |
output_path = process_video(
|
| 261 |
st.session_state.uploaded_video,
|
| 262 |
background,
|
| 263 |
bg_type=bg_type
|
| 264 |
)
|
| 265 |
-
|
| 266 |
if output_path and os.path.exists(output_path):
|
| 267 |
st.session_state.processed_video_path = output_path
|
| 268 |
process_status.success("β
Video processing complete!")
|
| 269 |
else:
|
| 270 |
process_status.error("β Failed to process video. Please check the logs for details.")
|
| 271 |
-
|
| 272 |
except Exception as e:
|
| 273 |
process_status.error(f"β An error occurred: {str(e)}")
|
| 274 |
logger.exception("Video processing failed")
|
| 275 |
finally:
|
| 276 |
st.session_state.processing = False
|
| 277 |
# ------- FORM WRAP END --------
|
| 278 |
-
|
| 279 |
# Show processed video if available (outside form, stable)
|
| 280 |
if 'processed_video_path' in st.session_state and st.session_state.processed_video_path:
|
| 281 |
st.markdown("### Processed Video")
|
|
@@ -294,5 +320,6 @@ def main():
|
|
| 294 |
st.error(f"Error displaying video: {str(e)}")
|
| 295 |
logger.error(f"Error displaying video: {str(e)}", exc_info=True)
|
| 296 |
|
|
|
|
| 297 |
if __name__ == "__main__":
|
| 298 |
-
main()
|
|
|
|
| 1 |
# streamlit_ui.py
|
| 2 |
+
|
| 3 |
+
# --- Imports ---
|
| 4 |
import streamlit as st
|
| 5 |
import os
|
| 6 |
import sys
|
|
|
|
| 12 |
from PIL import Image
|
| 13 |
import logging
|
| 14 |
import io
|
| 15 |
+
import torch
|
| 16 |
+
import traceback
|
| 17 |
+
from concurrent.futures import ThreadPoolExecutor
|
| 18 |
|
| 19 |
+
# --- Project Setup ---
|
| 20 |
sys.path.append(str(Path(__file__).parent.absolute()))
|
| 21 |
|
| 22 |
+
# --- Logging Configuration ---
|
| 23 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 24 |
logger = logging.getLogger(__name__)
|
| 25 |
|
| 26 |
+
# --- Global Exception Hook ---
|
| 27 |
+
def custom_excepthook(type, value, tb):
|
| 28 |
+
logger.error(f"Unhandled: {type.__name__}: {value}\n{''.join(traceback.format_tb(tb))}", exc_info=True)
|
| 29 |
+
sys.excepthook = custom_excepthook
|
| 30 |
+
threading.excepthook = custom_excepthook # Python 3.10+
|
| 31 |
+
|
| 32 |
+
# --- Streamlit Page Config ---
|
| 33 |
st.set_page_config(
|
| 34 |
page_title="π¬ Advanced Video Background Replacer",
|
| 35 |
page_icon="π₯",
|
|
|
|
| 37 |
initial_sidebar_state="expanded"
|
| 38 |
)
|
| 39 |
|
| 40 |
+
# --- Custom CSS ---
|
| 41 |
st.markdown("""
|
| 42 |
<style>
|
| 43 |
.main .block-container {
|
|
|
|
| 84 |
</style>
|
| 85 |
""", unsafe_allow_html=True)
|
| 86 |
|
| 87 |
+
# --- Session State Initialization ---
|
| 88 |
def initialize_session_state():
|
| 89 |
"""Initialize all session state variables"""
|
| 90 |
if 'uploaded_video' not in st.session_state:
|
|
|
|
| 102 |
if 'progress_text' not in st.session_state:
|
| 103 |
st.session_state.progress_text = "Ready"
|
| 104 |
|
| 105 |
+
# --- Video Upload Handling ---
|
| 106 |
def handle_video_upload():
|
| 107 |
"""Handle video file upload"""
|
| 108 |
uploaded = st.file_uploader(
|
| 109 |
+
"πΉ Upload Video",
|
| 110 |
type=["mp4", "mov", "avi"],
|
| 111 |
key="video_uploader"
|
| 112 |
)
|
| 113 |
if uploaded is not None:
|
| 114 |
st.session_state.uploaded_video = uploaded
|
| 115 |
|
| 116 |
+
# --- Video Preview ---
|
| 117 |
def show_video_preview():
|
| 118 |
"""Show video preview in the UI"""
|
| 119 |
st.markdown("### Video Preview")
|
|
|
|
| 122 |
st.video(video_bytes)
|
| 123 |
st.session_state.uploaded_video.seek(0)
|
| 124 |
|
| 125 |
+
# --- Background Selection ---
|
| 126 |
def handle_background_selection():
|
| 127 |
"""Handle background selection UI"""
|
| 128 |
st.markdown("### Background Options")
|
|
|
|
| 142 |
if bg_image is not None:
|
| 143 |
st.session_state.bg_image = Image.open(bg_image)
|
| 144 |
st.image(
|
| 145 |
+
st.session_state.bg_image,
|
| 146 |
+
caption="Selected Background",
|
| 147 |
use_container_width=True
|
| 148 |
)
|
| 149 |
|
| 150 |
elif bg_type == "Color":
|
| 151 |
st.session_state.bg_color = st.color_picker(
|
| 152 |
+
"π¨ Choose Background Color",
|
| 153 |
st.session_state.bg_color
|
| 154 |
)
|
| 155 |
color_rgb = tuple(int(st.session_state.bg_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
|
|
|
|
| 159 |
|
| 160 |
return bg_type.lower()
|
| 161 |
|
| 162 |
+
# --- Video Processing ---
|
| 163 |
+
@st.fragment(run_if=lambda: st.session_state.processing)
|
| 164 |
def process_video(input_file, background, bg_type="image"):
|
| 165 |
"""
|
| 166 |
Process video with the selected background using SAM2 and MatAnyone pipeline.
|
|
|
|
| 173 |
|
| 174 |
# Save the uploaded video to a temporary file
|
| 175 |
input_path = str(temp_dir / "input.mp4")
|
| 176 |
+
logger.info(f"Writing video to {input_path}")
|
| 177 |
with open(input_path, "wb") as f:
|
| 178 |
+
written = f.write(input_file.getvalue())
|
| 179 |
+
logger.info(f"Wrote {written/1e6:.2f}MB")
|
| 180 |
+
if not os.path.exists(input_path):
|
| 181 |
+
raise FileNotFoundError(f"Input video not saved: {input_path}")
|
| 182 |
|
| 183 |
# Prepare background
|
| 184 |
bg_path = None
|
|
|
|
| 194 |
bg_path = str(temp_dir / "background.jpg")
|
| 195 |
cv2.imwrite(bg_path, np.ones((100, 100, 3), dtype=np.uint8) * color_rgb[::-1])
|
| 196 |
|
| 197 |
+
# Set up progress placeholders
|
| 198 |
+
progress_placeholder = st.empty()
|
| 199 |
+
status_placeholder = st.empty()
|
| 200 |
|
| 201 |
def progress_callback(progress, message):
|
| 202 |
progress = max(0, min(1, float(progress)))
|
| 203 |
+
progress_placeholder.progress(progress)
|
| 204 |
+
status_placeholder.text(f"Status: {message}")
|
| 205 |
+
|
| 206 |
+
# Log GPU state
|
| 207 |
+
logger.info(f"CUDA Available: {torch.cuda.is_available()}, Device: {torch.cuda.get_device_name(0)}")
|
| 208 |
+
logger.info(f"GPU Mem Before: {torch.cuda.memory_allocated()/1e9:.2f}GB")
|
| 209 |
|
| 210 |
# Process the video
|
| 211 |
output_path = str(temp_dir / "output.mp4")
|
|
|
|
|
|
|
| 212 |
click_points = [[0.5, 0.5]]
|
|
|
|
|
|
|
| 213 |
from pipeline.integrated_pipeline import TwoStageProcessor
|
| 214 |
|
| 215 |
+
# Cache processor
|
| 216 |
+
@st.cache_resource
|
| 217 |
+
def load_processor(temp_dir):
|
| 218 |
+
return TwoStageProcessor(temp_dir=temp_dir)
|
| 219 |
+
processor = load_processor(str(temp_dir))
|
| 220 |
|
| 221 |
+
# Run in thread to catch exceptions
|
| 222 |
+
with ThreadPoolExecutor(max_workers=1) as executor:
|
| 223 |
+
future = executor.submit(
|
| 224 |
+
processor.process_video,
|
| 225 |
+
input_video=input_path,
|
| 226 |
+
background_video=bg_path if bg_type == "image" else "",
|
| 227 |
+
click_points=click_points,
|
| 228 |
+
output_path=output_path,
|
| 229 |
+
use_matanyone=True,
|
| 230 |
+
progress_callback=progress_callback
|
| 231 |
+
)
|
| 232 |
+
try:
|
| 233 |
+
success = future.result(timeout=300) # 5min timeout
|
| 234 |
+
except Exception as e:
|
| 235 |
+
logger.error(f"Pipeline thread fail: {traceback.format_exc()}", exc_info=True)
|
| 236 |
+
raise
|
| 237 |
+
|
| 238 |
+
logger.info(f"GPU Mem After: {torch.cuda.memory_allocated()/1e9:.2f}GB")
|
| 239 |
+
torch.cuda.empty_cache() # Free memory
|
| 240 |
|
| 241 |
if not success:
|
| 242 |
raise RuntimeError("Video processing failed")
|
| 243 |
|
|
|
|
| 244 |
return output_path
|
| 245 |
|
| 246 |
except Exception as e:
|
|
|
|
| 248 |
st.error(f"An error occurred during processing: {str(e)}")
|
| 249 |
return None
|
| 250 |
|
| 251 |
+
# --- Main Application ---
|
| 252 |
def main():
|
| 253 |
st.title("π¬ Advanced Video Background Replacer")
|
| 254 |
st.markdown("---")
|
|
|
|
| 267 |
with col2:
|
| 268 |
st.header("2. Background Settings")
|
| 269 |
bg_type = handle_background_selection()
|
|
|
|
| 270 |
st.header("3. Process & Download")
|
| 271 |
# ------- FORM WRAP START --------
|
| 272 |
with st.form("process_form"):
|
|
|
|
| 274 |
"π Process Video",
|
| 275 |
disabled=not st.session_state.uploaded_video or st.session_state.processing
|
| 276 |
)
|
|
|
|
| 277 |
process_status = st.empty()
|
| 278 |
if submitted and not st.session_state.processing:
|
| 279 |
st.session_state.processing = True
|
|
|
|
| 285 |
background = st.session_state.bg_image
|
| 286 |
elif bg_type == "color" and 'bg_color' in st.session_state:
|
| 287 |
background = st.session_state.bg_color
|
|
|
|
| 288 |
# Process the video
|
| 289 |
output_path = process_video(
|
| 290 |
st.session_state.uploaded_video,
|
| 291 |
background,
|
| 292 |
bg_type=bg_type
|
| 293 |
)
|
|
|
|
| 294 |
if output_path and os.path.exists(output_path):
|
| 295 |
st.session_state.processed_video_path = output_path
|
| 296 |
process_status.success("β
Video processing complete!")
|
| 297 |
else:
|
| 298 |
process_status.error("β Failed to process video. Please check the logs for details.")
|
|
|
|
| 299 |
except Exception as e:
|
| 300 |
process_status.error(f"β An error occurred: {str(e)}")
|
| 301 |
logger.exception("Video processing failed")
|
| 302 |
finally:
|
| 303 |
st.session_state.processing = False
|
| 304 |
# ------- FORM WRAP END --------
|
|
|
|
| 305 |
# Show processed video if available (outside form, stable)
|
| 306 |
if 'processed_video_path' in st.session_state and st.session_state.processed_video_path:
|
| 307 |
st.markdown("### Processed Video")
|
|
|
|
| 320 |
st.error(f"Error displaying video: {str(e)}")
|
| 321 |
logger.error(f"Error displaying video: {str(e)}", exc_info=True)
|
| 322 |
|
| 323 |
+
# --- Entry Point ---
|
| 324 |
if __name__ == "__main__":
|
| 325 |
+
main()
|