ROI选区01
This commit is contained in:
142
api/alarm.py
Normal file
142
api/alarm.py
Normal file
@@ -0,0 +1,142 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from db.crud import (
|
||||
create_alarm,
|
||||
get_alarm_stats,
|
||||
get_alarms,
|
||||
update_alarm,
|
||||
)
|
||||
from db.models import get_db
|
||||
from inference.pipeline import get_pipeline
|
||||
|
||||
router = APIRouter(prefix="/api/alarms", tags=["告警管理"])
|
||||
|
||||
|
||||
@router.get("", response_model=List[dict])
|
||||
def list_alarms(
|
||||
camera_id: Optional[int] = None,
|
||||
event_type: Optional[str] = None,
|
||||
limit: int = Query(default=100, le=1000),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
alarms = get_alarms(db, camera_id=camera_id, event_type=event_type, limit=limit, offset=offset)
|
||||
return [
|
||||
{
|
||||
"id": alarm.id,
|
||||
"camera_id": alarm.camera_id,
|
||||
"roi_id": alarm.roi_id,
|
||||
"event_type": alarm.event_type,
|
||||
"confidence": alarm.confidence,
|
||||
"snapshot_path": alarm.snapshot_path,
|
||||
"llm_checked": alarm.llm_checked,
|
||||
"llm_result": alarm.llm_result,
|
||||
"processed": alarm.processed,
|
||||
"created_at": alarm.created_at.isoformat() if alarm.created_at else None,
|
||||
}
|
||||
for alarm in alarms
|
||||
]
|
||||
|
||||
|
||||
@router.get("/stats")
|
||||
def get_alarm_statistics(db: Session = Depends(get_db)):
|
||||
stats = get_alarm_stats(db)
|
||||
return stats
|
||||
|
||||
|
||||
@router.get("/{alarm_id}", response_model=dict)
|
||||
def get_alarm(alarm_id: int, db: Session = Depends(get_db)):
|
||||
from db.crud import get_alarms
|
||||
alarms = get_alarms(db, limit=1)
|
||||
alarm = next((a for a in alarms if a.id == alarm_id), None)
|
||||
if not alarm:
|
||||
raise HTTPException(status_code=404, detail="告警不存在")
|
||||
return {
|
||||
"id": alarm.id,
|
||||
"camera_id": alarm.camera_id,
|
||||
"roi_id": alarm.roi_id,
|
||||
"event_type": alarm.event_type,
|
||||
"confidence": alarm.confidence,
|
||||
"snapshot_path": alarm.snapshot_path,
|
||||
"llm_checked": alarm.llm_checked,
|
||||
"llm_result": alarm.llm_result,
|
||||
"processed": alarm.processed,
|
||||
"created_at": alarm.created_at.isoformat() if alarm.created_at else None,
|
||||
}
|
||||
|
||||
|
||||
@router.put("/{alarm_id}")
|
||||
def update_alarm_status(
|
||||
alarm_id: int,
|
||||
llm_checked: Optional[bool] = None,
|
||||
llm_result: Optional[str] = None,
|
||||
processed: Optional[bool] = None,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
alarm = update_alarm(db, alarm_id, llm_checked=llm_checked, llm_result=llm_result, processed=processed)
|
||||
if not alarm:
|
||||
raise HTTPException(status_code=404, detail="告警不存在")
|
||||
return {"message": "更新成功"}
|
||||
|
||||
|
||||
@router.post("/{alarm_id}/llm-check")
|
||||
async def trigger_llm_check(alarm_id: int, db: Session = Depends(get_db)):
|
||||
from db.crud import get_alarms
|
||||
alarms = get_alarms(db, limit=1)
|
||||
alarm = next((a for a in alarms if a.id == alarm_id), None)
|
||||
if not alarm:
|
||||
raise HTTPException(status_code=404, detail="告警不存在")
|
||||
|
||||
if not alarm.snapshot_path or not os.path.exists(alarm.snapshot_path):
|
||||
raise HTTPException(status_code=400, detail="截图不存在")
|
||||
|
||||
try:
|
||||
from config import get_config
|
||||
config = get_config()
|
||||
if not config.llm.enabled:
|
||||
raise HTTPException(status_code=400, detail="大模型功能未启用")
|
||||
|
||||
import base64
|
||||
with open(alarm.snapshot_path, "rb") as f:
|
||||
img_base64 = base64.b64encode(f.read()).decode("utf-8")
|
||||
|
||||
from openai import OpenAI
|
||||
client = OpenAI(
|
||||
api_key=config.llm.api_key,
|
||||
base_url=config.llm.base_url,
|
||||
)
|
||||
|
||||
prompt = """分析这张监控截图,判断是否存在异常行为。请简要说明:
|
||||
1. 画面中是否有人
|
||||
2. 人员位置和行为
|
||||
3. 是否存在异常"""
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model=config.llm.model,
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{img_base64}"}},
|
||||
{"type": "text", "text": prompt},
|
||||
],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
result = response.choices[0].message.content
|
||||
update_alarm(db, alarm_id, llm_checked=True, llm_result=result)
|
||||
|
||||
return {"message": "大模型分析完成", "result": result}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"大模型调用失败: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/queue/size")
|
||||
def get_event_queue_size():
|
||||
pipeline = get_pipeline()
|
||||
return {"size": len(pipeline.event_queue), "max_size": pipeline.config.inference.event_queue_maxlen}
|
||||
160
api/camera.py
Normal file
160
api/camera.py
Normal file
@@ -0,0 +1,160 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from db.crud import (
|
||||
create_camera,
|
||||
delete_camera,
|
||||
get_all_cameras,
|
||||
get_camera_by_id,
|
||||
update_camera,
|
||||
)
|
||||
from db.models import get_db
|
||||
from inference.pipeline import get_pipeline
|
||||
|
||||
router = APIRouter(prefix="/api/cameras", tags=["摄像头管理"])
|
||||
|
||||
|
||||
@router.get("", response_model=List[dict])
|
||||
def list_cameras(
|
||||
enabled_only: bool = True,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
cameras = get_all_cameras(db, enabled_only=enabled_only)
|
||||
return [
|
||||
{
|
||||
"id": cam.id,
|
||||
"name": cam.name,
|
||||
"rtsp_url": cam.rtsp_url,
|
||||
"enabled": cam.enabled,
|
||||
"fps_limit": cam.fps_limit,
|
||||
"process_every_n_frames": cam.process_every_n_frames,
|
||||
"created_at": cam.created_at.isoformat() if cam.created_at else None,
|
||||
}
|
||||
for cam in cameras
|
||||
]
|
||||
|
||||
|
||||
@router.get("/{camera_id}", response_model=dict)
|
||||
def get_camera(camera_id: int, db: Session = Depends(get_db)):
|
||||
camera = get_camera_by_id(db, camera_id)
|
||||
if not camera:
|
||||
raise HTTPException(status_code=404, detail="摄像头不存在")
|
||||
return {
|
||||
"id": camera.id,
|
||||
"name": camera.name,
|
||||
"rtsp_url": camera.rtsp_url,
|
||||
"enabled": camera.enabled,
|
||||
"fps_limit": camera.fps_limit,
|
||||
"process_every_n_frames": camera.process_every_n_frames,
|
||||
"created_at": camera.created_at.isoformat() if camera.created_at else None,
|
||||
}
|
||||
|
||||
|
||||
@router.post("", response_model=dict)
|
||||
def add_camera(
|
||||
name: str,
|
||||
rtsp_url: str,
|
||||
fps_limit: int = 30,
|
||||
process_every_n_frames: int = 3,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
camera = create_camera(
|
||||
db,
|
||||
name=name,
|
||||
rtsp_url=rtsp_url,
|
||||
fps_limit=fps_limit,
|
||||
process_every_n_frames=process_every_n_frames,
|
||||
)
|
||||
|
||||
if camera.enabled:
|
||||
pipeline = get_pipeline()
|
||||
pipeline.add_camera(camera)
|
||||
|
||||
return {
|
||||
"id": camera.id,
|
||||
"name": camera.name,
|
||||
"rtsp_url": camera.rtsp_url,
|
||||
"enabled": camera.enabled,
|
||||
}
|
||||
|
||||
|
||||
@router.put("/{camera_id}", response_model=dict)
|
||||
def modify_camera(
|
||||
camera_id: int,
|
||||
name: Optional[str] = None,
|
||||
rtsp_url: Optional[str] = None,
|
||||
fps_limit: Optional[int] = None,
|
||||
process_every_n_frames: Optional[int] = None,
|
||||
enabled: Optional[bool] = None,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
camera = update_camera(
|
||||
db,
|
||||
camera_id=camera_id,
|
||||
name=name,
|
||||
rtsp_url=rtsp_url,
|
||||
fps_limit=fps_limit,
|
||||
process_every_n_frames=process_every_n_frames,
|
||||
enabled=enabled,
|
||||
)
|
||||
if not camera:
|
||||
raise HTTPException(status_code=404, detail="摄像头不存在")
|
||||
|
||||
pipeline = get_pipeline()
|
||||
if enabled is True:
|
||||
pipeline.add_camera(camera)
|
||||
elif enabled is False:
|
||||
pipeline.remove_camera(camera_id)
|
||||
|
||||
return {
|
||||
"id": camera.id,
|
||||
"name": camera.name,
|
||||
"rtsp_url": camera.rtsp_url,
|
||||
"enabled": camera.enabled,
|
||||
}
|
||||
|
||||
|
||||
@router.delete("/{camera_id}")
|
||||
def remove_camera(camera_id: int, db: Session = Depends(get_db)):
|
||||
pipeline = get_pipeline()
|
||||
pipeline.remove_camera(camera_id)
|
||||
|
||||
if not delete_camera(db, camera_id):
|
||||
raise HTTPException(status_code=404, detail="摄像头不存在")
|
||||
|
||||
return {"message": "删除成功"}
|
||||
|
||||
|
||||
@router.get("/{camera_id}/status")
|
||||
def get_camera_status(camera_id: int, db: Session = Depends(get_db)):
|
||||
from db.crud import get_camera_status as get_status
|
||||
|
||||
status = get_status(db, camera_id)
|
||||
pipeline = get_pipeline()
|
||||
|
||||
stream = pipeline.stream_manager.get_stream(str(camera_id))
|
||||
if stream:
|
||||
stream_info = stream.get_info()
|
||||
else:
|
||||
stream_info = None
|
||||
|
||||
if status:
|
||||
return {
|
||||
"camera_id": camera_id,
|
||||
"is_running": status.is_running,
|
||||
"fps": status.fps,
|
||||
"error_message": status.error_message,
|
||||
"last_check_time": status.last_check_time.isoformat() if status.last_check_time else None,
|
||||
"stream": stream_info,
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"camera_id": camera_id,
|
||||
"is_running": False,
|
||||
"fps": 0.0,
|
||||
"error_message": None,
|
||||
"last_check_time": None,
|
||||
"stream": stream_info,
|
||||
}
|
||||
192
api/roi.py
Normal file
192
api/roi.py
Normal file
@@ -0,0 +1,192 @@
|
||||
import json
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from db.crud import (
|
||||
create_roi,
|
||||
delete_roi,
|
||||
get_all_rois,
|
||||
get_roi_by_id,
|
||||
get_roi_points,
|
||||
update_roi,
|
||||
)
|
||||
from db.models import get_db
|
||||
from inference.pipeline import get_pipeline
|
||||
from inference.roi.roi_filter import ROIFilter
|
||||
|
||||
router = APIRouter(prefix="/api/camera", tags=["ROI管理"])
|
||||
|
||||
|
||||
@router.get("/{camera_id}/rois", response_model=List[dict])
|
||||
def list_rois(camera_id: int, db: Session = Depends(get_db)):
|
||||
roi_configs = get_all_rois(db, camera_id)
|
||||
return [
|
||||
{
|
||||
"id": roi.id,
|
||||
"roi_id": roi.roi_id,
|
||||
"name": roi.name,
|
||||
"type": roi.roi_type,
|
||||
"points": json.loads(roi.points),
|
||||
"rule": roi.rule_type,
|
||||
"direction": roi.direction,
|
||||
"stay_time": roi.stay_time,
|
||||
"enabled": roi.enabled,
|
||||
"threshold_sec": roi.threshold_sec,
|
||||
"confirm_sec": roi.confirm_sec,
|
||||
"return_sec": roi.return_sec,
|
||||
}
|
||||
for roi in roi_configs
|
||||
]
|
||||
|
||||
|
||||
@router.get("/{camera_id}/roi/{roi_id}", response_model=dict)
|
||||
def get_roi(camera_id: int, roi_id: int, db: Session = Depends(get_db)):
|
||||
roi = get_roi_by_id(db, roi_id)
|
||||
if not roi:
|
||||
raise HTTPException(status_code=404, detail="ROI不存在")
|
||||
return {
|
||||
"id": roi.id,
|
||||
"roi_id": roi.roi_id,
|
||||
"name": roi.name,
|
||||
"type": roi.roi_type,
|
||||
"points": json.loads(roi.points),
|
||||
"rule": roi.rule_type,
|
||||
"direction": roi.direction,
|
||||
"stay_time": roi.stay_time,
|
||||
"enabled": roi.enabled,
|
||||
"threshold_sec": roi.threshold_sec,
|
||||
"confirm_sec": roi.confirm_sec,
|
||||
"return_sec": roi.return_sec,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{camera_id}/roi", response_model=dict)
|
||||
def add_roi(
|
||||
camera_id: int,
|
||||
roi_id: str,
|
||||
name: str,
|
||||
roi_type: str,
|
||||
points: List[List[float]],
|
||||
rule_type: str,
|
||||
direction: Optional[str] = None,
|
||||
stay_time: Optional[int] = None,
|
||||
threshold_sec: int = 360,
|
||||
confirm_sec: int = 30,
|
||||
return_sec: int = 5,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
roi = create_roi(
|
||||
db,
|
||||
camera_id=camera_id,
|
||||
roi_id=roi_id,
|
||||
name=name,
|
||||
roi_type=roi_type,
|
||||
points=points,
|
||||
rule_type=rule_type,
|
||||
direction=direction,
|
||||
stay_time=stay_time,
|
||||
threshold_sec=threshold_sec,
|
||||
confirm_sec=confirm_sec,
|
||||
return_sec=return_sec,
|
||||
)
|
||||
|
||||
pipeline = get_pipeline()
|
||||
pipeline.roi_filter.update_cache(camera_id, get_roi_points(db, camera_id))
|
||||
|
||||
return {
|
||||
"id": roi.id,
|
||||
"roi_id": roi.roi_id,
|
||||
"name": roi.name,
|
||||
"type": roi.roi_type,
|
||||
"points": points,
|
||||
"rule": roi.rule_type,
|
||||
"enabled": roi.enabled,
|
||||
}
|
||||
|
||||
|
||||
@router.put("/{camera_id}/roi/{roi_id}", response_model=dict)
|
||||
def modify_roi(
|
||||
camera_id: int,
|
||||
roi_id: int,
|
||||
name: Optional[str] = None,
|
||||
points: Optional[List[List[float]]] = None,
|
||||
rule_type: Optional[str] = None,
|
||||
direction: Optional[str] = None,
|
||||
stay_time: Optional[int] = None,
|
||||
enabled: Optional[bool] = None,
|
||||
threshold_sec: Optional[int] = None,
|
||||
confirm_sec: Optional[int] = None,
|
||||
return_sec: Optional[int] = None,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
roi = update_roi(
|
||||
db,
|
||||
roi_id=roi_id,
|
||||
name=name,
|
||||
points=points,
|
||||
rule_type=rule_type,
|
||||
direction=direction,
|
||||
stay_time=stay_time,
|
||||
enabled=enabled,
|
||||
threshold_sec=threshold_sec,
|
||||
confirm_sec=confirm_sec,
|
||||
return_sec=return_sec,
|
||||
)
|
||||
if not roi:
|
||||
raise HTTPException(status_code=404, detail="ROI不存在")
|
||||
|
||||
pipeline = get_pipeline()
|
||||
pipeline.roi_filter.update_cache(camera_id, get_roi_points(db, camera_id))
|
||||
|
||||
return {
|
||||
"id": roi.id,
|
||||
"roi_id": roi.roi_id,
|
||||
"name": roi.name,
|
||||
"type": roi.roi_type,
|
||||
"points": json.loads(roi.points),
|
||||
"rule": roi.rule_type,
|
||||
"enabled": roi.enabled,
|
||||
}
|
||||
|
||||
|
||||
@router.delete("/{camera_id}/roi/{roi_id}")
|
||||
def remove_roi(camera_id: int, roi_id: int, db: Session = Depends(get_db)):
|
||||
if not delete_roi(db, roi_id):
|
||||
raise HTTPException(status_code=404, detail="ROI不存在")
|
||||
|
||||
pipeline = get_pipeline()
|
||||
pipeline.roi_filter.update_cache(camera_id, get_roi_points(db, camera_id))
|
||||
|
||||
return {"message": "删除成功"}
|
||||
|
||||
|
||||
@router.get("/{camera_id}/roi/validate")
|
||||
def validate_roi(
|
||||
camera_id: int,
|
||||
roi_type: str,
|
||||
points: List[List[float]],
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
try:
|
||||
if roi_type == "polygon":
|
||||
from shapely.geometry import Polygon
|
||||
poly = Polygon(points)
|
||||
is_valid = poly.is_valid
|
||||
area = poly.area
|
||||
elif roi_type == "line":
|
||||
from shapely.geometry import LineString
|
||||
line = LineString(points)
|
||||
is_valid = True
|
||||
area = 0.0
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="不支持的ROI类型")
|
||||
|
||||
return {
|
||||
"valid": is_valid,
|
||||
"area": area,
|
||||
"message": "有效" if is_valid else "无效:自交或自重叠",
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
Reference in New Issue
Block a user