Spaces:
Running
Running
File size: 7,892 Bytes
2758540 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | """
app.py - Main FastAPI Application Entry Point
Multimodal Surveillance Intelligence System
Run with: uvicorn app:app --host 0.0.0.0 --port 8000 --reload
"""
import asyncio
import time
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from loguru import logger
import psutil
import os
from config import settings, DEVICE
# ββ Global Singletons (initialized on startup) ββββββββββββββββββββββββββββββββ
vision_pipeline = None
search_engine = None
qa_system = None
report_generator = None
summarizer = None
movement_graph = None
audio_asr = None
audio_classifier = None
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application startup/shutdown lifecycle."""
global vision_pipeline, search_engine, qa_system, report_generator
global summarizer, movement_graph, audio_asr, audio_classifier
logger.info("=" * 60)
logger.info(f"π Starting {settings.APP_NAME} v{settings.APP_VERSION}")
logger.info(f" Device: {DEVICE}")
logger.info("=" * 60)
# 1. Initialize database tables
logger.info("π¦ Initializing database...")
from database.session import create_tables
await create_tables()
# 2. Load Vision Pipeline
logger.info("π₯ Loading Vision Pipeline...")
from vision.pipeline import VisionPipeline
vision_pipeline = VisionPipeline()
# 3. Load NLP Components
logger.info("π¬ Loading NLP: Semantic Search...")
from nlp.search import SemanticSearchEngine
search_engine = SemanticSearchEngine()
logger.info("β Loading NLP: QA System...")
from nlp.qa import SurveillanceQA
qa_system = SurveillanceQA()
logger.info("π Loading NLP: Report Generator...")
from nlp.report import IncidentReportGenerator
report_generator = IncidentReportGenerator()
logger.info("π Loading NLP: Summarizer...")
from nlp.summarizer import SurveillanceSummarizer
summarizer = SurveillanceSummarizer()
# 4. Load Graph Module
logger.info("πΈοΈ Initializing Movement Graph...")
from graph.movement_graph import MovementGraph
movement_graph = MovementGraph()
# 5. Load Audio (optional)
if settings.ENABLE_AUDIO:
logger.info("ποΈ Loading Audio Module...")
from audio.audio_module import WhisperASR, AudioClassifier
audio_asr = WhisperASR()
audio_classifier = AudioClassifier()
logger.info("β
All components loaded successfully!")
logger.info(f"π Memory usage: {psutil.Process().memory_info().rss / 1e6:.1f} MB")
yield # App is running
# Shutdown
logger.info("π Shutting down Surveillance System...")
from vision.stream_manager import stream_manager
stream_manager.shutdown()
# ββ FastAPI App ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
app = FastAPI(
title=settings.APP_NAME,
version=settings.APP_VERSION,
description="""
**Multimodal Surveillance Intelligence System**
Capabilities:
- π₯ Real-time multi-camera video processing
- π€ Person detection (DETR) + Multi-object tracking (ByteTrack)
- π Cross-camera Re-Identification (ViT + FAISS)
- π Clothing/attribute recognition (CLIP zero-shot)
- π¬ Semantic search over surveillance logs
- β Natural language Q&A over events
- π Automated incident report generation
- πΈοΈ Movement graph anomaly detection
""",
lifespan=lifespan,
docs_url="/docs",
redoc_url="/redoc",
)
# ββ CORS βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
app.add_middleware(
CORSMiddleware,
allow_origins=settings.CORS_ORIGINS + ["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ββ Request Latency Logging Middleware βββββββββββββββββββββββββββββββββββββββββ
@app.middleware("http")
async def log_requests(request: Request, call_next):
t0 = time.perf_counter()
response = await call_next(request)
latency = (time.perf_counter() - t0) * 1000
logger.debug(f"{request.method} {request.url.path} β {response.status_code} ({latency:.1f}ms)")
response.headers["X-Process-Time-Ms"] = f"{latency:.2f}"
return response
# ββ Register Routers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
from routes.vision_routes import router as vision_router
from routes.nlp_routes import router as nlp_router
from routes.stream_routes import router as stream_router
app.include_router(vision_router)
app.include_router(nlp_router)
app.include_router(stream_router)
# ββ Static Files βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Create static directory if it doesn't exist
os.makedirs("static/thumbnails", exist_ok=True)
app.mount("/static", StaticFiles(directory="static"), name="static")
# ββ Health & Status Routes βββββββββββββββββββββββββββββββββββββββββββββββββββββ
@app.get("/", tags=["Health"])
async def root():
return {
"system": settings.APP_NAME,
"version": settings.APP_VERSION,
"status": "operational",
"device": str(DEVICE),
"docs": "/docs",
}
@app.get("/health", tags=["Health"])
async def health_check():
mem = psutil.Process().memory_info().rss / 1e6
cpu = psutil.cpu_percent(interval=0.1)
import torch
gpu_info = {}
if torch.cuda.is_available():
gpu_info = {
"name": torch.cuda.get_device_name(0),
"memory_allocated_mb": round(torch.cuda.memory_allocated(0) / 1e6, 1),
"memory_reserved_mb": round(torch.cuda.memory_reserved(0) / 1e6, 1),
}
return {
"status": "healthy",
"device": str(DEVICE),
"memory_mb": round(mem, 1),
"cpu_percent": cpu,
"gpu": gpu_info,
"models_loaded": {
"vision_pipeline": vision_pipeline is not None,
"search_engine": search_engine is not None,
"qa_system": qa_system is not None,
"report_generator": report_generator is not None,
"summarizer": summarizer is not None,
"movement_graph": movement_graph is not None,
"audio": audio_asr is not None,
},
}
@app.get("/metrics", tags=["Health"])
async def prometheus_metrics():
"""Basic Prometheus-style metrics."""
from prometheus_client import generate_latest, CONTENT_TYPE_LATEST
from starlette.responses import Response
return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)
# ββ Entry point ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app:app",
host=settings.HOST,
port=settings.PORT,
reload=settings.DEBUG,
workers=1, # 1 worker required for shared model singletons
log_level="info",
)
|