Add action model video endpoint + new config file

This commit is contained in:
sosokker 2024-04-28 02:09:29 +07:00
parent 92484d9e43
commit 97785a7ebc
5 changed files with 117 additions and 40 deletions

View File

@ -9,16 +9,22 @@ Attributes:
MINIO_ENDPOINT: The endpoint of the MinIO storage MINIO_ENDPOINT: The endpoint of the MinIO storage
MINIO_ACCESS_KEY: The access key of the MinIO storage MINIO_ACCESS_KEY: The access key of the MinIO storage
MINIO_SECRET_KEY: The secret key of the MinIO storage MINIO_SECRET_KEY: The secret key of the MinIO storage
VIDEO_BUCKET: The bucket name for storing video files
""" """
from decouple import Config from decouple import config
config = Config('.env')
DB_HOST = config.get('DB_HOST', default='localhost') DB_HOST = config('DB_HOST')
DB_USER = config.get('DB_USER', default='root') DB_USER = config('DB_USER')
DB_PASSWD = config.get('DB_PASSWD', default='root') DB_PASSWD = config('DB_PASSWD')
DB_NAME = config.get('DB_NAME') DB_NAME = config('DB_NAME')
MINIO_ENDPOINT = config.get('MINIO_ENDPOINT', default='localhost:9000') MINIO_ENDPOINT = config('MINIO_ENDPOINT')
MINIO_ACCESS_KEY = config.get('MINIO_ACCESS_KEY') MINIO_ACCESS_KEY = config('MINIO_ACCESS_KEY')
MINIO_SECRET_KEY = config.get('MINIO_SECRET_KEY') MINIO_SECRET_KEY = config('MINIO_SECRET_KEY')
VIDEO_BUCKET = config('VIDEO_BUCKET')
TEMP_VIDEO_FILE = config('TEMP_VIDEO_FILE')
CONFIG_FILE = config('CONFIG_FILE')
YOLO_WEIGHT_FILE = config('YOLO_WEIGHT_FILE')
SPPE_WEIGHT_FILE = config('SPPE_WEIGHT_FILE')
TSSTG_WEIGHT_FILE = config('TSSTG_WEIGHT_FILE')

View File

@ -3,13 +3,15 @@ import uvicorn
from fastapi import FastAPI from fastapi import FastAPI
from routers import video, weather from routers import video, weather
app = FastAPI( app = FastAPI(
title="Healthcare-System", title="Healthcare-System",
description="Hello Stranger.", description="Hello Stranger.",
root_path="/api/v1", root_path="/api/v1",
docs_url="/docs/swagger", docs_url="/docs/swagger",
openapi_url="/docs/openapi.json", openapi_url="/docs/openapi.json",
redoc_url="/docs" redoc_url="/docs",
lifespan=video.lifespan
) )
app.include_router(video.router, prefix="/camera") app.include_router(video.router, prefix="/camera")
@ -20,4 +22,4 @@ def read_root():
return {"Hello": "World"} return {"Hello": "World"}
if __name__ == "__main__": if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000) uvicorn.run("main:app", host="127.0.0.1", port=8000)

View File

@ -1,11 +1,34 @@
from cv2 import VideoCapture, imencode import cv2
import time
from fastapi import APIRouter, HTTPException from cv2 import VideoCapture, imencode
from contextlib import asynccontextmanager
from datetime import datetime
from threading import Thread
from fastapi import APIRouter, BackgroundTasks, FastAPI, HTTPException
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.jobstores.memory import MemoryJobStore
from database import minio_client
from config import TEMP_VIDEO_FILE, VIDEO_BUCKET
from scheme import Camera from scheme import Camera
from utils import save_to_config, read_cameras_from_config from utils import save_to_config, read_cameras_from_config
from analytic.action.action_model import generate_action_model_frame
jobstores = {
'default': MemoryJobStore()
}
scheduler = AsyncIOScheduler(jobstores=jobstores, timezone='Asia/Bangkok')
@asynccontextmanager
async def lifespan(application: FastAPI):
scheduler.start()
yield
scheduler.shutdown()
router = APIRouter() router = APIRouter()
@ -19,13 +42,40 @@ def generate_camera_id() -> int:
cameras.sort(key=lambda x: x.camera_id) cameras.sort(key=lambda x: x.camera_id)
return cameras[-1].camera_id + 1 return cameras[-1].camera_id + 1
def upload_to_minio(camera_id):
current_time = datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
temp_file_name = f"camera_{camera_id}_{current_time}.avi"
minio_client.fput_object(VIDEO_BUCKET, temp_file_name, f"{TEMP_VIDEO_FILE}_{camera_id}.avi")
# --- BACKGROUND SCHEDULER ---
# TODO: Save video to Minio
@scheduler.scheduled_job('interval', seconds=60)
def check_camera_status():
"""
Check if the camera is available or not
If the camera is available, set status to True
else set status to False
"""
global cameras
for camera in cameras:
cap = VideoCapture(camera.link)
if not cap.isOpened() or cap is None:
camera.status = False
else:
camera.status = True
cap.release()
save_to_config(key="cameras", value=cameras)
# --- ROUTER ENDPOINTS --- # --- ROUTER ENDPOINTS ---
@router.post("/add", response_model=dict) @router.post("/add", response_model=dict)
async def add_camera(rtsp_link: str): async def add_camera(link: str):
if rtsp_link: if link:
id = generate_camera_id() id = generate_camera_id()
camera = Camera(camera_id=id, link=rtsp_link) camera = Camera(camera_id=id, link=link, status=False)
cameras.append(camera) cameras.append(camera)
save_to_config(key="cameras", value=cameras) save_to_config(key="cameras", value=cameras)
return {"message": "Camera added successfully", "camera_id": id} return {"message": "Camera added successfully", "camera_id": id}
@ -40,33 +90,49 @@ async def list_cameras() -> list[Camera]:
@router.get("/stream/{camera_id}") @router.get("/stream/{camera_id}")
async def stream_video(camera_id: int) -> StreamingResponse: async def stream_video(camera_id: int) -> StreamingResponse:
available_cameras = [camera.camera_id for camera in cameras] camera = next((c for c in cameras if c.camera_id == camera_id), None)
if camera_id in available_cameras: if not camera:
for camera in cameras: raise HTTPException(status_code=404, detail="Camera not found")
if camera.camera_id == camera_id:
link = camera.link if not camera.status:
break raise HTTPException(status_code=400, detail="Camera is not available")
cap = VideoCapture(link)
if not cap.isOpened(): cap = VideoCapture(camera.link)
raise HTTPException(status_code=404, detail="Camera is closed or not available") if not cap.isOpened():
raise HTTPException(status_code=404, detail="Camera is closed or not available")
def generate_frames():
while cap.isOpened(): def generate_frames():
ret, frame = cap.read() while cap.isOpened():
if not ret: ret, frame = cap.read()
break if not ret:
ret, buffer = imencode('.jpg', frame) raise HTTPException(status_code=500, detail="Connection to camera lost")
if not ret: ret, buffer = imencode('.jpg', frame)
break if not ret:
yield b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + buffer.tobytes() + b'\r\n' raise HTTPException(status_code=500, detail="Connection to camera lost")
yield b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + buffer.tobytes() + b'\r\n'
return StreamingResponse(generate_frames(), media_type="multipart/x-mixed-replace; boundary=frame") return StreamingResponse(generate_frames(), media_type="multipart/x-mixed-replace; boundary=frame")
else:
raise HTTPException(status_code=404, detail="No cameras available")
@router.get("/stream/action/{camera_id}")
async def stream_action_video(camera_id: int) -> StreamingResponse:
camera = next((c for c in cameras if c.camera_id == camera_id), None)
if not camera:
raise HTTPException(status_code=404, detail="Camera not found")
if not camera.status:
raise HTTPException(status_code=400, detail="Camera is not available")
cap = VideoCapture(camera.link)
if not cap.isOpened():
raise HTTPException(status_code=404, detail="Camera is closed or not available")
return StreamingResponse(generate_action_model_frame(camera.link), media_type="multipart/x-mixed-replace; boundary=frame")
@router.delete("/remove/{camera_id}", response_model=dict) @router.delete("/remove/{camera_id}", response_model=dict)
async def disconnect_camera(camera_id: int) -> dict: async def disconnect_camera(camera_id: int) -> dict:
global video_writer
for camera in cameras: for camera in cameras:
if camera.camera_id == camera_id: if camera.camera_id == camera_id:
cameras.remove(camera) cameras.remove(camera)

View File

@ -63,4 +63,5 @@ class AverageIndoorData(BaseModel):
class Camera(BaseModel): class Camera(BaseModel):
camera_id: int camera_id: int
link: str link: str
status: bool = False

View File

@ -5,4 +5,6 @@ sqlalchemy
python-decouple python-decouple
tqdm tqdm
scipy scipy
matplotlib matplotlib
pymysql
apscheduler