Spaces:
Running
Running
| """ | |
| src/streamlit_with_api.py | |
| Professional Streamlit UI for DeepGuard AI - DeepFake Detection System | |
| Integrated FastAPI Backend - Starts API in background thread | |
| Run with: streamlit run src/streamlit_with_api.py | |
| """ | |
| import streamlit as st | |
| import requests | |
| from io import BytesIO | |
| import tempfile | |
| import os | |
| import sys | |
| import threading | |
| import time | |
| import uvicorn | |
| # Page configuration - MUST be first Streamlit command | |
| st.set_page_config( | |
| page_title="DeepGuard AI - DeepFake Detection", | |
| page_icon="π‘οΈ", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # Add project root to path for imports | |
| ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| if ROOT_DIR not in sys.path: | |
| sys.path.insert(0, ROOT_DIR) | |
| # Import FastAPI app | |
| try: | |
| # Add app directory to path | |
| app_dir = os.path.join(ROOT_DIR, "app") | |
| if app_dir not in sys.path: | |
| sys.path.insert(0, app_dir) | |
| # Import the FastAPI app | |
| import main | |
| fastapi_app = main.app | |
| FASTAPI_AVAILABLE = True | |
| except ImportError as e: | |
| st.error(f"FastAPI not available: {e}") | |
| FASTAPI_AVAILABLE = False | |
| # π₯ HF-Compatible Flow: Start FastAPI in background thread | |
| def start_api(): | |
| """Start FastAPI server in background thread""" | |
| if FASTAPI_AVAILABLE: | |
| try: | |
| uvicorn.run( | |
| fastapi_app, | |
| host="127.0.0.1", | |
| port=8000, | |
| log_level="warning" | |
| ) | |
| except Exception as e: | |
| print(f"Error starting FastAPI: {e}") | |
| # Start FastAPI only once per session | |
| if FASTAPI_AVAILABLE and "api_started" not in st.session_state: | |
| api_thread = threading.Thread(target=start_api, daemon=True) | |
| api_thread.start() | |
| st.session_state.api_started = True | |
| time.sleep(2) # Give API time to start | |
| # Custom CSS for professional look | |
| st.markdown(""" | |
| <style> | |
| /* Main container styling */ | |
| .main { | |
| padding-top: 0rem; | |
| background-color: #f3f4f6; /* light gray for better contrast */ | |
| } | |
| /* Header styling */ | |
| .header { | |
| background: linear-gradient(135deg, #1f2937 0%, #4f46e5 100%); | |
| padding: 2rem 0; | |
| margin: -1rem -1rem 2rem -1rem; | |
| box-shadow: 0 4px 6px rgba(15, 23, 42, 0.35); | |
| } | |
| .header-content { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 0 2rem; | |
| } | |
| .header-title { | |
| color: #f9fafb; | |
| font-size: 3rem; | |
| font-weight: bold; | |
| margin: 0; | |
| text-align: center; | |
| text-shadow: 2px 2px 6px rgba(15, 23, 42, 0.6); | |
| } | |
| .header-subtitle { | |
| color: rgba(249, 250, 251, 0.9); | |
| font-size: 1.2rem; | |
| text-align: center; | |
| margin-top: 0.5rem; | |
| } | |
| /* Footer styling */ | |
| .footer { | |
| background: linear-gradient(135deg, #111827 0%, #1f2937 100%); | |
| color: #e5e7eb; | |
| padding: 2rem 0; | |
| margin: 3rem -1rem -1rem -1rem; | |
| text-align: center; | |
| box-shadow: 0 -4px 6px rgba(15, 23, 42, 0.4); | |
| } | |
| .footer-content { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 0 2rem; | |
| } | |
| .footer-text { | |
| color: rgba(229, 231, 235, 0.9); | |
| font-size: 1rem; | |
| margin: 0; | |
| } | |
| /* Card styling */ | |
| .card { | |
| background: #ffffff; | |
| border-radius: 10px; | |
| padding: 2rem; | |
| box-shadow: 0 2px 10px rgba(15, 23, 42, 0.12); | |
| margin-bottom: 2rem; | |
| } | |
| /* Button styling */ | |
| .stButton > button { | |
| background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); | |
| color: #f9fafb; | |
| border: none; | |
| border-radius: 6px; | |
| padding: 0.6rem 2.2rem; | |
| font-weight: 600; | |
| letter-spacing: 0.02em; | |
| transition: all 0.2s ease-in-out; | |
| } | |
| .stButton > button:hover { | |
| transform: translateY(-1px); | |
| box-shadow: 0 6px 14px rgba(79, 70, 229, 0.35); | |
| } | |
| /* File uploader styling */ | |
| .uploadedFile { | |
| border: 2px dashed #4f46e5; | |
| border-radius: 10px; | |
| padding: 1rem; | |
| background-color: #eef2ff; | |
| } | |
| /* Success/Error message styling */ | |
| .stSuccess, .stError { | |
| border-radius: 6px; | |
| } | |
| /* Hide Streamlit default elements */ | |
| #MainMenu {visibility: hidden;} | |
| footer {visibility: hidden;} | |
| header {visibility: hidden;} | |
| /* Info boxes */ | |
| .info-box { | |
| background: linear-gradient(135deg, #e5edff 0%, #d1fae5 100%); | |
| padding: 1.6rem; | |
| border-radius: 12px; | |
| margin: 1rem 0; | |
| border-left: 5px solid #4f46e5; | |
| color: #111827; | |
| } | |
| .info-box h3, | |
| .info-box p { | |
| color: #111827; | |
| } | |
| .feature-box { | |
| background: #ffffff; | |
| padding: 1.6rem; | |
| border-radius: 12px; | |
| margin: 1rem 0; | |
| box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08); | |
| border: 1px solid #e5e7eb; | |
| color: #111827; | |
| } | |
| .feature-box h4, | |
| .feature-box li { | |
| color: #111827; | |
| } | |
| /* Status indicator */ | |
| .status-indicator { | |
| display: inline-block; | |
| width: 12px; | |
| height: 12px; | |
| border-radius: 50%; | |
| margin-right: 8px; | |
| } | |
| .status-ready { | |
| background-color: #10b981; | |
| } | |
| .status-loading { | |
| background-color: #f59e0b; | |
| animation: pulse 2s infinite; | |
| } | |
| .status-error { | |
| background-color: #ef4444; | |
| } | |
| @keyframes pulse { | |
| 0% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| 100% { opacity: 1; } | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Header | |
| st.markdown(""" | |
| <div class="header"> | |
| <div class="header-content"> | |
| <h1 class="header-title">π‘οΈ DeepGuard AI</h1> | |
| <p class="header-subtitle">Advanced DeepFake Detection System | Integrated FastAPI Backend</p> | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Sidebar for configuration | |
| with st.sidebar: | |
| st.markdown("### βοΈ Configuration") | |
| # API Status | |
| st.markdown("### π Model Status") | |
| if FASTAPI_AVAILABLE and "api_started" in st.session_state: | |
| st.markdown('<span class="status-indicator status-ready"></span>FastAPI: Running (127.0.0.1:8000)', unsafe_allow_html=True) | |
| st.markdown('<span class="status-indicator status-ready"></span>Video Model: Optimized', unsafe_allow_html=True) | |
| st.markdown('<span class="status-indicator status-error"></span>Image Model: Disabled', unsafe_allow_html=True) | |
| else: | |
| st.markdown('<span class="status-indicator status-error"></span>FastAPI: Not Available', unsafe_allow_html=True) | |
| st.markdown("---") | |
| # β API URL - Same as requested | |
| API_URL = "http://127.0.0.1:8000/predict" | |
| st.text_input( | |
| "API Endpoint", | |
| value=API_URL, | |
| disabled=True, | |
| help="FastAPI running internally on port 8000" | |
| ) | |
| st.markdown("---") | |
| st.markdown("### π Supported Formats") | |
| st.markdown(""" | |
| **Images:** | |
| - JPG, JPEG, PNG | |
| **Videos:** | |
| - MP4, AVI, MOV, MKV | |
| """) | |
| st.markdown("---") | |
| st.markdown("### βΉοΈ About") | |
| st.markdown(""" | |
| DeepGuard AI uses advanced deep learning | |
| models to detect deepfake content in | |
| videos with high accuracy. | |
| **Memory Optimized Version:** | |
| - β Image processing disabled | |
| - β Video processing only | |
| - π 50-70% less memory usage | |
| """) | |
| # Main content area | |
| st.markdown(""" | |
| <div class="info-box"> | |
| <h3>π How It Works</h3> | |
| <p>Upload a video file to analyze. Our AI-powered system will detect if the content is real or a deepfake using state-of-the-art neural networks. This version is optimized for memory efficiency with video-only processing.</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # File upload section | |
| st.markdown("### π€ Upload Video for Analysis") | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| uploaded_file = st.file_uploader( | |
| "Choose a video file", | |
| type=["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm"], | |
| help="Upload a video file to detect deepfakes (Image processing disabled)", | |
| label_visibility="collapsed" | |
| ) | |
| with col2: | |
| st.markdown(""" | |
| <div class="feature-box"> | |
| <h4>β¨ Features</h4> | |
| <ul> | |
| <li>Video DeepFake Detection</li> | |
| <li>Memory Optimized</li> | |
| <li>Real-time Analysis</li> | |
| <li>50-70% Less Memory Usage</li> | |
| <li>Fast Processing</li> | |
| </ul> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Display uploaded file and process | |
| if uploaded_file is not None: | |
| st.markdown("---") | |
| # Always video (since only videos are allowed) | |
| file_extension = uploaded_file.name.split('.')[-1].lower() | |
| col1, col2 = st.columns([2, 1]) | |
| with col1: | |
| st.markdown("### πΉ Uploaded Video") | |
| st.info(f"Video file: {uploaded_file.name} ({uploaded_file.size / (1024*1024):.2f} MB)") | |
| st.markdown("**Note:** Video processing optimized for memory efficiency.") | |
| with col2: | |
| st.markdown("### π File Information") | |
| st.metric("File Name", uploaded_file.name) | |
| st.metric("File Size", f"{uploaded_file.size / (1024*1024):.2f} MB") | |
| st.metric("File Type", "Video") | |
| st.metric("Processing", "Optimized") | |
| # Analyze button | |
| st.markdown("---") | |
| col1, col2, col3 = st.columns([1, 2, 1]) | |
| with col2: | |
| analyze_button = st.button( | |
| "π Analyze Video for DeepFake", | |
| use_container_width=True, | |
| type="primary" | |
| ) | |
| if analyze_button: | |
| # Save uploaded file temporarily | |
| with tempfile.NamedTemporaryFile(delete=False, suffix=f".{file_extension}") as tmp_file: | |
| tmp_file.write(uploaded_file.getvalue()) | |
| tmp_path = tmp_file.name | |
| try: | |
| # Prepare file for upload | |
| files = { | |
| "file": ( | |
| uploaded_file.name, | |
| open(tmp_path, "rb"), | |
| uploaded_file.type if hasattr(uploaded_file, 'type') else f"video/{file_extension}" | |
| ) | |
| } | |
| # Show progress | |
| with st.spinner("π Analyzing video... Memory optimized processing..."): | |
| response = requests.post(API_URL, files=files, timeout=180) # Longer timeout for videos | |
| # Close file | |
| files["file"][1].close() | |
| if response.status_code == 200: | |
| result = response.json() | |
| # Display results in a nice format | |
| st.markdown("---") | |
| st.markdown("### π Analysis Results") | |
| # Prediction result | |
| prediction = result.get("prediction", "unknown") | |
| probabilities = result.get("probabilities", [[0, 0]]) | |
| # Color coding | |
| if prediction.lower() == "fake": | |
| prediction_color = "π΄" | |
| prediction_bg = "#ffebee" | |
| else: | |
| prediction_color = "π’" | |
| prediction_bg = "#e8f5e9" | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| st.markdown(f""" | |
| <div style="background: {prediction_bg}; padding: 2rem; border-radius: 10px; text-align: center; box-shadow: 0 2px 10px rgba(0,0,0,0.1);"> | |
| <h2 style="margin: 0; color: #333;">{prediction_color} Prediction</h2> | |
| <h1 style="margin: 0.5rem 0; color: #333; text-transform: uppercase;">{prediction}</h1> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col2: | |
| # Probability display | |
| if len(probabilities) > 0 and len(probabilities[0]) >= 2: | |
| real_prob = probabilities[0][0] * 100 | |
| fake_prob = probabilities[0][1] * 100 | |
| st.markdown("### π Confidence Scores") | |
| st.progress(real_prob / 100, text=f"Real: {real_prob:.2f}%") | |
| st.progress(fake_prob / 100, text=f"Fake: {fake_prob:.2f}%") | |
| # Detailed results | |
| with st.expander("π View Detailed Results"): | |
| st.json(result) | |
| # Success message | |
| st.success("β Analysis completed successfully!") | |
| else: | |
| st.error(f"β API Error {response.status_code}: {response.text}") | |
| except requests.exceptions.Timeout: | |
| st.error("β±οΈ Request timed out. The file might be too large or the server is busy.") | |
| except requests.exceptions.ConnectionError: | |
| st.error("π Connection error. Please make sure the API server is running.") | |
| except Exception as e: | |
| st.error(f"β Error: {str(e)}") | |
| finally: | |
| # Clean up temp file | |
| try: | |
| os.unlink(tmp_path) | |
| except: | |
| pass | |
| else: | |
| # Show placeholder when no file is uploaded | |
| st.markdown(""" | |
| <div style="text-align: center; padding: 4rem 2rem; background: #f8f9fa; border-radius: 10px; margin: 2rem 0;"> | |
| <h3 style="color: #667eea;">π Upload a file to get started</h3> | |
| <p style="color: #666;">Select an image or video file from your device to analyze for deepfake content</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Footer | |
| st.markdown(""" | |
| <div class="footer"> | |
| <div class="footer-content"> | |
| <p class="footer-text">This project made by <strong>ABESH MEENA</strong></p> | |
| <p class="footer-text" style="margin-top: 0.5rem; font-size: 0.9rem; opacity: 0.8;"> | |
| DeepGuard AI - Advanced DeepFake Detection System | Powered by Deep Learning | |
| </p> | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |