feat: initial BantayCam scaffold

- FastAPI backend with async SQLAlchemy
- Camera RTSP management (add, start, stop)
- Vehicle detection (YOLO + fast-alpr)
  - Type: car, motorcycle, truck, jeepney
  - Color detection (HSV)
  - License plate OCR
  - Motorcycle person count
- Face detection + InsightFace ArcFace embedding
- pgvector identity grouping (auto-cluster same face)
- Vehicle + person movement trail APIs
- Docker Compose with pgvector/pg16
- Models: Camera, VehicleIdentity, VehicleEvent, PersonIdentity, FaceEvent
This commit is contained in:
Nemo
2026-03-12 12:11:02 +08:00
commit 7c43e4580d
24 changed files with 1650 additions and 0 deletions

View File

View File

@@ -0,0 +1,42 @@
from pydantic_settings import BaseSettings
from typing import Optional
class Settings(BaseSettings):
APP_NAME: str = "BantayCam"
APP_VERSION: str = "0.1.0"
DEBUG: bool = False
# Database
DATABASE_URL: str = "postgresql+asyncpg://bantaycam:bantaycam@localhost:5432/bantaycam"
# Storage (MinIO / local)
STORAGE_TYPE: str = "local" # "local" or "minio"
STORAGE_PATH: str = "./snapshots"
MINIO_ENDPOINT: str = ""
MINIO_ACCESS_KEY: str = ""
MINIO_SECRET_KEY: str = ""
MINIO_BUCKET: str = "bantaycam"
# AI Models
YOLO_MODEL: str = "yolov8n.pt" # Vehicle + person detection
PLATE_MODEL: str = "yolov8n.pt" # Plate detection (custom PH)
FACE_MODEL: str = "buffalo_l" # InsightFace model
GPU_DEVICE: int = 0 # -1 for CPU, 0 for first GPU
# Processing
PROCESS_EVERY_N_FRAMES: int = 5 # Skip frames for performance
FACE_SIMILARITY_THRESHOLD: float = 0.5 # Cosine distance threshold
PLATE_CONFIDENCE_THRESHOLD: float = 0.6
DETECTION_CONFIDENCE_THRESHOLD: float = 0.5
# Alerts
TELEGRAM_BOT_TOKEN: Optional[str] = None
TELEGRAM_CHAT_ID: Optional[str] = None
class Config:
env_file = ".env"
case_sensitive = True
settings = Settings()

View File

@@ -0,0 +1,34 @@
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase
from app.core.config import settings
engine = create_async_engine(
settings.DATABASE_URL,
echo=settings.DEBUG,
pool_pre_ping=True,
)
AsyncSessionLocal = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
class Base(DeclarativeBase):
pass
async def get_db():
async with AsyncSessionLocal() as session:
try:
yield session
finally:
await session.close()
async def init_db():
"""Create all tables on startup."""
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)