Spaces:
Running
Running
feat: full clearning the repo
Browse files- Dockerfile +1 -1
- README.md +3 -1
- api/__pycache__/dependencies.cpython-311.pyc +0 -0
- api/routers/__pycache__/camera_stream.cpython-311.pyc +0 -0
- api/routers/__pycache__/dashboard_stream.cpython-311.pyc +0 -0
- api/routers/__pycache__/health.cpython-311.pyc +0 -0
- api/routers/__pycache__/metrics.cpython-311.pyc +0 -0
- api/routers/camera_stream.py +5 -27
- config/__pycache__/settings.cpython-311.pyc +0 -0
- config/config.yaml +10 -19
- config/settings.py +12 -63
- contracts/__pycache__/camera_metadata.cpython-311.pyc +0 -0
- domain/__pycache__/detector.cpython-311.pyc +0 -0
- domain/__pycache__/logger.cpython-311.pyc +0 -0
- domain/__pycache__/logger.cpython-314.pyc +0 -0
- infra/__pycache__/logger_structlog.cpython-311.pyc +0 -0
- infra/__pycache__/logger_structlog.cpython-314.pyc +0 -0
- infra/__pycache__/system_metrics.cpython-311.pyc +0 -0
- infra/__pycache__/system_metrics.cpython-314.pyc +0 -0
- main.py +17 -22
- requirements.txt +10 -5
- utils/__pycache__/experiment.cpython-311.pyc +0 -0
- utils/experiment.py +5 -6
Dockerfile
CHANGED
|
@@ -18,5 +18,5 @@ COPY . .
|
|
| 18 |
# As HF want for backend.
|
| 19 |
EXPOSE 7860
|
| 20 |
|
| 21 |
-
#
|
| 22 |
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860", "--ws", "websockets"]
|
|
|
|
| 18 |
# As HF want for backend.
|
| 19 |
EXPOSE 7860
|
| 20 |
|
| 21 |
+
# 0.0.0.0 accepts connections from anywhere, not just local. also don't forget --ws to enable sockets on docker
|
| 22 |
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860", "--ws", "websockets"]
|
README.md
CHANGED
|
@@ -5,4 +5,6 @@ app_port: 7860
|
|
| 5 |
pinned: false
|
| 6 |
---
|
| 7 |
|
| 8 |
-
|
|
|
|
|
|
|
|
|
| 5 |
pinned: false
|
| 6 |
---
|
| 7 |
|
| 8 |
+
Tracking System Backend Deployed on huggingface.
|
| 9 |
+
|
| 10 |
+
This repository is being modified only from the main Github Repo using CI/CD, Making sure it is always up to date and match project requirements.
|
api/__pycache__/dependencies.cpython-311.pyc
DELETED
|
Binary file (1.13 kB)
|
|
|
api/routers/__pycache__/camera_stream.cpython-311.pyc
DELETED
|
Binary file (10.5 kB)
|
|
|
api/routers/__pycache__/dashboard_stream.cpython-311.pyc
DELETED
|
Binary file (3.01 kB)
|
|
|
api/routers/__pycache__/health.cpython-311.pyc
DELETED
|
Binary file (2.73 kB)
|
|
|
api/routers/__pycache__/metrics.cpython-311.pyc
DELETED
|
Binary file (1.4 kB)
|
|
|
api/routers/camera_stream.py
CHANGED
|
@@ -30,7 +30,7 @@ async def websocket_detect(
|
|
| 30 |
|
| 31 |
url here is: ws://127.0.0.1:8000/detectors/stream/camera_id
|
| 32 |
"""
|
| 33 |
-
# Yes, I asked the same questions, is using webscoket.app.state many times here is consuming.
|
| 34 |
state = websocket.app.state
|
| 35 |
logger = state.logger
|
| 36 |
# Using Depends is important and called Inversion Of Control (IoC)/ Dependency injection, and is important for testing.
|
|
@@ -72,37 +72,26 @@ async def websocket_detect(
|
|
| 72 |
|
| 73 |
async def process_frames():
|
| 74 |
|
| 75 |
-
|
| 76 |
try:
|
| 77 |
-
# What are the info you aim to collect from the camera?
|
| 78 |
-
# How many frames received by second.
|
| 79 |
-
# Frame processing time.
|
| 80 |
-
# Average processing time for logger.
|
| 81 |
-
# Model processing time.
|
| 82 |
-
|
| 83 |
-
# frame_count = itertools.count()
|
| 84 |
|
| 85 |
logger.info(f"Camera {camera_id} start sending frames...")
|
| 86 |
|
| 87 |
-
def decode_frame():
|
| 88 |
-
# Decode image
|
| 89 |
-
return cv.imdecode(np.frombuffer(frame_bytes, np.uint8), cv.IMREAD_COLOR)
|
| 90 |
|
| 91 |
# Keep receiving messages in a loop until disconnection.
|
| 92 |
while True:
|
| 93 |
-
|
| 94 |
frame_bytes = await frame_queue.get()
|
| 95 |
|
| 96 |
# Profiling
|
| 97 |
t0 = time.time()
|
| 98 |
|
| 99 |
-
image_array = await loop.run_in_executor(None, decode_frame)
|
| 100 |
decode_duration_seconds.labels(camera_id).observe(round(time.time() - t0, 3))
|
| 101 |
mlflow.log_metric("frame_processing_time", round(time.time() - t0, 3), next(step_counter))
|
| 102 |
|
|
|
|
| 103 |
detection_task = loop.run_in_executor(None, detector.detect, image_array)
|
| 104 |
safety_task = loop.run_in_executor(None, safety_detector.detect, image_array)
|
| 105 |
-
|
| 106 |
detections, safety_detection = await asyncio.gather(detection_task, safety_task)
|
| 107 |
detection_duration_seconds.labels(camera_id).observe(round(time.time() - t0, 3))
|
| 108 |
mlflow.log_metric("detection_duration_seconds", round(time.time() - t0, 3), next(step_counter))
|
|
@@ -129,7 +118,6 @@ async def websocket_detect(
|
|
| 129 |
detection_metadata = [DetectionMetadata(depth=depth, xRatio=xRatio) for depth, xRatio in zip(depth_points, boxes_center_ratio)]
|
| 130 |
metadata = CameraMetadata(camera_id=camera_id, is_danger = True if safety_detection else False, detection_metadata=detection_metadata)
|
| 131 |
|
| 132 |
-
# state.camera_metadata[camera_id] = metadata.model_dump()
|
| 133 |
await redis.publish("dashboard_stream", metadata.model_dump_json())
|
| 134 |
# Even if the camera was disconnected, redis is still going to show its data, which is not accurate.
|
| 135 |
# Instead, we set expiry date for the camera data.
|
|
@@ -154,7 +142,6 @@ async def websocket_detect(
|
|
| 154 |
|
| 155 |
except WebSocketDisconnect:
|
| 156 |
logger.warn(f"Client ID >>{camera_id}<< Disconnected Normally...")
|
| 157 |
-
# state.camera_metadata.pop(camera_id, None)
|
| 158 |
|
| 159 |
except Exception as e:
|
| 160 |
logger.error(f"Error in websocker, Client ID: >>{camera_id}<<: {e}")
|
|
@@ -164,13 +151,4 @@ async def websocket_detect(
|
|
| 164 |
|
| 165 |
finally:
|
| 166 |
active_cameras.dec()
|
| 167 |
-
mlflow.end_run()
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
# Uncomment this when needed, It is the same but using HTTP, which is Request Response only. could be used for testing.
|
| 171 |
-
# from fastapi import Request, UploadFile
|
| 172 |
-
# @router.post("/detect")
|
| 173 |
-
# async def post_detection(request: Request, file: UploadFile):
|
| 174 |
-
# # Request here is being used to access the app.state.model
|
| 175 |
-
|
| 176 |
-
# request.app.state.model.detect(file)
|
|
|
|
| 30 |
|
| 31 |
url here is: ws://127.0.0.1:8000/detectors/stream/camera_id
|
| 32 |
"""
|
| 33 |
+
# Yes, I asked the same questions, is using webscoket.app.state many times here is consuming. after checking, it is not performance consuming.
|
| 34 |
state = websocket.app.state
|
| 35 |
logger = state.logger
|
| 36 |
# Using Depends is important and called Inversion Of Control (IoC)/ Dependency injection, and is important for testing.
|
|
|
|
| 72 |
|
| 73 |
async def process_frames():
|
| 74 |
|
|
|
|
| 75 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
logger.info(f"Camera {camera_id} start sending frames...")
|
| 78 |
|
| 79 |
+
def decode_frame(fb): return cv.imdecode(np.frombuffer(fb, np.uint8), cv.IMREAD_COLOR)
|
|
|
|
|
|
|
| 80 |
|
| 81 |
# Keep receiving messages in a loop until disconnection.
|
| 82 |
while True:
|
|
|
|
| 83 |
frame_bytes = await frame_queue.get()
|
| 84 |
|
| 85 |
# Profiling
|
| 86 |
t0 = time.time()
|
| 87 |
|
| 88 |
+
image_array = await loop.run_in_executor(None, decode_frame, frame_bytes)
|
| 89 |
decode_duration_seconds.labels(camera_id).observe(round(time.time() - t0, 3))
|
| 90 |
mlflow.log_metric("frame_processing_time", round(time.time() - t0, 3), next(step_counter))
|
| 91 |
|
| 92 |
+
# Apply detection models
|
| 93 |
detection_task = loop.run_in_executor(None, detector.detect, image_array)
|
| 94 |
safety_task = loop.run_in_executor(None, safety_detector.detect, image_array)
|
|
|
|
| 95 |
detections, safety_detection = await asyncio.gather(detection_task, safety_task)
|
| 96 |
detection_duration_seconds.labels(camera_id).observe(round(time.time() - t0, 3))
|
| 97 |
mlflow.log_metric("detection_duration_seconds", round(time.time() - t0, 3), next(step_counter))
|
|
|
|
| 118 |
detection_metadata = [DetectionMetadata(depth=depth, xRatio=xRatio) for depth, xRatio in zip(depth_points, boxes_center_ratio)]
|
| 119 |
metadata = CameraMetadata(camera_id=camera_id, is_danger = True if safety_detection else False, detection_metadata=detection_metadata)
|
| 120 |
|
|
|
|
| 121 |
await redis.publish("dashboard_stream", metadata.model_dump_json())
|
| 122 |
# Even if the camera was disconnected, redis is still going to show its data, which is not accurate.
|
| 123 |
# Instead, we set expiry date for the camera data.
|
|
|
|
| 142 |
|
| 143 |
except WebSocketDisconnect:
|
| 144 |
logger.warn(f"Client ID >>{camera_id}<< Disconnected Normally...")
|
|
|
|
| 145 |
|
| 146 |
except Exception as e:
|
| 147 |
logger.error(f"Error in websocker, Client ID: >>{camera_id}<<: {e}")
|
|
|
|
| 151 |
|
| 152 |
finally:
|
| 153 |
active_cameras.dec()
|
| 154 |
+
mlflow.end_run()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config/__pycache__/settings.cpython-311.pyc
DELETED
|
Binary file (5.34 kB)
|
|
|
config/config.yaml
CHANGED
|
@@ -1,27 +1,17 @@
|
|
| 1 |
-
# Shared defaults values that work for most people or envs
|
| 2 |
-
# TODO, mostly this might not work in docker, due to the "\", feel free to replace it by [*proj_dir, ai, dl_models] for exmple
|
| 3 |
|
| 4 |
project_name: Tracking Config
|
| 5 |
# This one called Literal block, there is another one called Folded (>)
|
| 6 |
project_desc: |
|
| 7 |
Tracking System for detection, providing real-time camera status throught a Dashboard.
|
| 8 |
task: indoor
|
| 9 |
-
|
| 10 |
intervals:
|
| 11 |
-
system_metrics_seconds: 3.0
|
| 12 |
-
frames_summary_every: 30
|
| 13 |
-
realtime_updates_every: 2
|
| 14 |
-
|
| 15 |
-
prometheus_port: 9091
|
| 16 |
-
|
| 17 |
-
paths:
|
| 18 |
-
# project_dir: &proj_dir G:\MyComputer\Workspace\Projects\gp-tracking-dashboard\tracking_dashboard
|
| 19 |
-
project_dir: &proj_dir .
|
| 20 |
-
# models_dir: &models_dir !join [*proj_dir, ai, dl_models]
|
| 21 |
-
# logs_dir: !join [*proj_dir, backend, config, logs, logs]
|
| 22 |
|
| 23 |
yolo:
|
| 24 |
-
|
| 25 |
classes:
|
| 26 |
- person
|
| 27 |
batch_size: 16
|
|
@@ -31,11 +21,12 @@ yolo:
|
|
| 31 |
data_path: ""
|
| 32 |
|
| 33 |
security_detector:
|
| 34 |
-
|
| 35 |
classes:
|
| 36 |
- fire
|
| 37 |
- smoke
|
| 38 |
|
| 39 |
-
depth:
|
| 40 |
-
|
| 41 |
-
|
|
|
|
|
|
| 1 |
+
# YAML file contains Shared defaults values that work for most people or envs
|
|
|
|
| 2 |
|
| 3 |
project_name: Tracking Config
|
| 4 |
# This one called Literal block, there is another one called Folded (>)
|
| 5 |
project_desc: |
|
| 6 |
Tracking System for detection, providing real-time camera status throught a Dashboard.
|
| 7 |
task: indoor
|
|
|
|
| 8 |
intervals:
|
| 9 |
+
system_metrics_seconds: 3.0 # Logging system metrics every
|
| 10 |
+
frames_summary_every: 30 # Number of frames to create a logs summary
|
| 11 |
+
realtime_updates_every: 2 # backend send updates every n seconds?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
yolo:
|
| 14 |
+
model_name: "yolo26n.pt"
|
| 15 |
classes:
|
| 16 |
- person
|
| 17 |
batch_size: 16
|
|
|
|
| 21 |
data_path: ""
|
| 22 |
|
| 23 |
security_detector:
|
| 24 |
+
model_name: "yolo_smoke_fire.pt"
|
| 25 |
classes:
|
| 26 |
- fire
|
| 27 |
- smoke
|
| 28 |
|
| 29 |
+
depth:
|
| 30 |
+
model_name: "depth_anything_v2_vits.pth"
|
| 31 |
+
device: "cpu"
|
| 32 |
+
encoder: "vits"
|
config/settings.py
CHANGED
|
@@ -11,19 +11,18 @@ def join_tag(loader, node):
|
|
| 11 |
parts = loader.construct_sequence(node)
|
| 12 |
path = Path(*(str(part) for part in parts)).resolve()
|
| 13 |
return str(path)
|
|
|
|
| 14 |
# It didn't work before, After some research, .SafeLoaded is unmentioned must for my case.
|
| 15 |
yaml.SafeLoader.add_constructor("!join", join_tag)
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
# models_dir: str
|
| 22 |
-
# logs_dir: str
|
| 23 |
|
| 24 |
class YoloConfig(BaseModel):
|
| 25 |
"""Contains yolo configurations"""
|
| 26 |
-
|
| 27 |
classes: List[str]
|
| 28 |
batch_size: int
|
| 29 |
epochs: int
|
|
@@ -33,19 +32,15 @@ class YoloConfig(BaseModel):
|
|
| 33 |
|
| 34 |
class SecurityDetector(BaseModel):
|
| 35 |
"Contains Security Detectors like Smoke - Fire"
|
| 36 |
-
|
| 37 |
classes: List[str]
|
| 38 |
|
| 39 |
class DepthConfig(BaseModel):
|
| 40 |
"Contains depths estimation configurations"
|
| 41 |
-
|
|
|
|
| 42 |
encoder: Literal["vits", "vitb", "vitl", "vitg"]
|
| 43 |
|
| 44 |
-
class IntervalsConfig(BaseModel):
|
| 45 |
-
system_metrics_seconds: float
|
| 46 |
-
frames_summary_every: int
|
| 47 |
-
realtime_updates_every: float
|
| 48 |
-
|
| 49 |
|
| 50 |
class AppConfig(BaseSettings):
|
| 51 |
"""
|
|
@@ -59,33 +54,22 @@ class AppConfig(BaseSettings):
|
|
| 59 |
env_file=Path(__file__).parent / ".env",
|
| 60 |
env_file_encoding="utf-8",
|
| 61 |
yaml_file=Path(__file__).parent / "config.yaml",
|
| 62 |
-
|
| 63 |
-
# env_prefix="YOLO_", # Means configs we are talking about starts with YOLO_
|
| 64 |
-
# env_nested_delimiter="__", # Means we use _ instead of spaces for the same var
|
| 65 |
-
extra="ignore" # Ignore other settings in yaml and env as they are not mentioedhere
|
| 66 |
)
|
| 67 |
|
| 68 |
project_name:str
|
| 69 |
project_desc:str
|
| 70 |
task: Literal["indoor", "outdoor"]
|
| 71 |
|
| 72 |
-
paths: PathsConfig
|
| 73 |
yolo: YoloConfig
|
| 74 |
security_detector: SecurityDetector
|
| 75 |
depth: DepthConfig
|
| 76 |
intervals: IntervalsConfig
|
| 77 |
redis_url:str
|
| 78 |
|
| 79 |
-
# Backend
|
| 80 |
-
|
| 81 |
-
|
| 82 |
@classmethod
|
| 83 |
def settings_customise_sources(cls,
|
| 84 |
settings_cls: type[BaseSettings], # Base param.
|
| 85 |
-
# init_settings: PydanticBaseSettingsSource, # Values passed to __init__
|
| 86 |
-
# env_settings: PydanticBaseSettingsSource, # OS Env variables
|
| 87 |
-
# dotenv_settings: PydanticBaseSettingsSource,
|
| 88 |
-
# file_secret_settings: PydanticBaseSettingsSource # Secret Directories
|
| 89 |
**kwargs
|
| 90 |
) -> tuple[PydanticBaseSettingsSource, ...] :
|
| 91 |
"""
|
|
@@ -93,7 +77,7 @@ class AppConfig(BaseSettings):
|
|
| 93 |
But this time it fixs the priority part, order by parameters priority.
|
| 94 |
"""
|
| 95 |
|
| 96 |
-
# Order by priority
|
| 97 |
return (
|
| 98 |
DotEnvSettingsSource(settings_cls), # Most important
|
| 99 |
EnvSettingsSource(settings_cls), # This allow for ex. hugging face to override .env values with its values.
|
|
@@ -101,43 +85,8 @@ class AppConfig(BaseSettings):
|
|
| 101 |
) # The return must be a tuple
|
| 102 |
|
| 103 |
|
| 104 |
-
# @classmethod
|
| 105 |
-
# def load_config(cls, yaml_path: Path | str = Path(__file__).parent / "config.yaml") -> "AppConfig":
|
| 106 |
-
# """Loading confiuration and settings from Config.yaml file then override using .env"""
|
| 107 |
-
|
| 108 |
-
# yaml_path = Path(yaml_path).resolve() # Absolute path
|
| 109 |
-
# if not yaml_path.is_file():
|
| 110 |
-
# raise FileNotFoundError(f"Config file not found: {yaml_path}")
|
| 111 |
-
|
| 112 |
-
# with yaml_path.open("r") as f:
|
| 113 |
-
# yaml_data = yaml.safe_load(f) or {}
|
| 114 |
-
|
| 115 |
-
# # env_data = cls() # This one loaded .env and not Yaml
|
| 116 |
-
# # When this project grow, you are going to create different types of .yaml files for products and debugging and so on
|
| 117 |
-
# # Feel free to stack them here, so we use the yaml required for our testing
|
| 118 |
-
# # Note that in debuging.yaml file, we only override the base, not starting from scratch.
|
| 119 |
-
# # return cls(**{
|
| 120 |
-
# # **yaml_data, # Loading config.yaml configurations
|
| 121 |
-
# # # **env_data.model_dump() # TODO(FIX) Overriding everything using .env
|
| 122 |
-
# # })
|
| 123 |
-
# return cls(**yaml_data)
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
if __name__ == "__main__":
|
| 129 |
-
|
| 130 |
-
# Checking for YAML part.
|
| 131 |
-
# config = AppConfig.load_config()
|
| 132 |
-
# print(config.model_dump())
|
| 133 |
-
# print(config.model_dump()["project_name"])
|
| 134 |
-
|
| 135 |
-
# Checking for .env file.
|
| 136 |
-
# config = AppConfig()
|
| 137 |
-
# print(f".env Path we are talking about: {Path(__file__).parent / ".env"}")
|
| 138 |
-
# print(config.model_config)
|
| 139 |
-
# print(config.project_name)
|
| 140 |
-
|
| 141 |
# Trying to checking both yaml and .env. This works really fine now.
|
| 142 |
config = AppConfig()
|
| 143 |
print(config.model_dump())
|
|
|
|
| 11 |
parts = loader.construct_sequence(node)
|
| 12 |
path = Path(*(str(part) for part in parts)).resolve()
|
| 13 |
return str(path)
|
| 14 |
+
|
| 15 |
# It didn't work before, After some research, .SafeLoaded is unmentioned must for my case.
|
| 16 |
yaml.SafeLoader.add_constructor("!join", join_tag)
|
| 17 |
|
| 18 |
+
class IntervalsConfig(BaseModel):
|
| 19 |
+
system_metrics_seconds: float
|
| 20 |
+
frames_summary_every: int
|
| 21 |
+
realtime_updates_every: float
|
|
|
|
|
|
|
| 22 |
|
| 23 |
class YoloConfig(BaseModel):
|
| 24 |
"""Contains yolo configurations"""
|
| 25 |
+
model_name: str
|
| 26 |
classes: List[str]
|
| 27 |
batch_size: int
|
| 28 |
epochs: int
|
|
|
|
| 32 |
|
| 33 |
class SecurityDetector(BaseModel):
|
| 34 |
"Contains Security Detectors like Smoke - Fire"
|
| 35 |
+
model_name: str
|
| 36 |
classes: List[str]
|
| 37 |
|
| 38 |
class DepthConfig(BaseModel):
|
| 39 |
"Contains depths estimation configurations"
|
| 40 |
+
model_name: str
|
| 41 |
+
device: Literal["cuda", "cpu"]
|
| 42 |
encoder: Literal["vits", "vitb", "vitl", "vitg"]
|
| 43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
class AppConfig(BaseSettings):
|
| 46 |
"""
|
|
|
|
| 54 |
env_file=Path(__file__).parent / ".env",
|
| 55 |
env_file_encoding="utf-8",
|
| 56 |
yaml_file=Path(__file__).parent / "config.yaml",
|
| 57 |
+
extra="ignore" # Ignore other settings in yaml and env as they are not mentioedhere
|
|
|
|
|
|
|
|
|
|
| 58 |
)
|
| 59 |
|
| 60 |
project_name:str
|
| 61 |
project_desc:str
|
| 62 |
task: Literal["indoor", "outdoor"]
|
| 63 |
|
|
|
|
| 64 |
yolo: YoloConfig
|
| 65 |
security_detector: SecurityDetector
|
| 66 |
depth: DepthConfig
|
| 67 |
intervals: IntervalsConfig
|
| 68 |
redis_url:str
|
| 69 |
|
|
|
|
|
|
|
|
|
|
| 70 |
@classmethod
|
| 71 |
def settings_customise_sources(cls,
|
| 72 |
settings_cls: type[BaseSettings], # Base param.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
**kwargs
|
| 74 |
) -> tuple[PydanticBaseSettingsSource, ...] :
|
| 75 |
"""
|
|
|
|
| 77 |
But this time it fixs the priority part, order by parameters priority.
|
| 78 |
"""
|
| 79 |
|
| 80 |
+
# Order by priority (first, more important)
|
| 81 |
return (
|
| 82 |
DotEnvSettingsSource(settings_cls), # Most important
|
| 83 |
EnvSettingsSource(settings_cls), # This allow for ex. hugging face to override .env values with its values.
|
|
|
|
| 85 |
) # The return must be a tuple
|
| 86 |
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
if __name__ == "__main__":
|
| 89 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
# Trying to checking both yaml and .env. This works really fine now.
|
| 91 |
config = AppConfig()
|
| 92 |
print(config.model_dump())
|
contracts/__pycache__/camera_metadata.cpython-311.pyc
DELETED
|
Binary file (999 Bytes)
|
|
|
domain/__pycache__/detector.cpython-311.pyc
DELETED
|
Binary file (1.12 kB)
|
|
|
domain/__pycache__/logger.cpython-311.pyc
DELETED
|
Binary file (1.36 kB)
|
|
|
domain/__pycache__/logger.cpython-314.pyc
DELETED
|
Binary file (1.66 kB)
|
|
|
infra/__pycache__/logger_structlog.cpython-311.pyc
DELETED
|
Binary file (3.22 kB)
|
|
|
infra/__pycache__/logger_structlog.cpython-314.pyc
DELETED
|
Binary file (3.82 kB)
|
|
|
infra/__pycache__/system_metrics.cpython-311.pyc
DELETED
|
Binary file (1.43 kB)
|
|
|
infra/__pycache__/system_metrics.cpython-314.pyc
DELETED
|
Binary file (1.27 kB)
|
|
|
main.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
-
from
|
| 2 |
-
# from prometheus_client import metrics
|
| 3 |
from ai.depth.depth_anything import DepthAnything
|
| 4 |
from ai.detectors.yolo_detector import YOLO_Detector
|
| 5 |
from config.settings import AppConfig
|
|
@@ -8,15 +7,16 @@ from infra.system_metrics import log_system_metrics
|
|
| 8 |
from api.routers import camera_stream
|
| 9 |
from api.routers import dashboard_stream
|
| 10 |
from api.routers import health
|
| 11 |
-
from contextlib import asynccontextmanager
|
| 12 |
from infra.logger_structlog import StructLogger
|
| 13 |
-
|
|
|
|
| 14 |
import mlflow
|
| 15 |
import torch
|
| 16 |
-
from huggingface_hub import hf_hub_download
|
| 17 |
import redis.asyncio as aioredis
|
| 18 |
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
| 19 |
import dagshub
|
|
|
|
|
|
|
| 20 |
|
| 21 |
@asynccontextmanager
|
| 22 |
async def lifespan(app: FastAPI):
|
|
@@ -26,28 +26,25 @@ async def lifespan(app: FastAPI):
|
|
| 26 |
|
| 27 |
settings = AppConfig()
|
| 28 |
logger = StructLogger(settings=settings)
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
logger.info("Starting Server.... ")
|
| 31 |
-
asyncio.create_task(log_system_metrics(logger, logger_interval_sec=settings.intervals.system_metrics_seconds))
|
| 32 |
|
| 33 |
-
|
| 34 |
-
detection_model_path = hf_hub_download(repo_id="Ultralytics/YOLO26", filename="yolo26n.pt")
|
| 35 |
app.state.detection_model = YOLO_Detector(detection_model_path)
|
| 36 |
|
| 37 |
-
depth_model_path =
|
| 38 |
-
app.state.depth_model = DepthAnything(encoder=settings.depth.encoder, depth_model_path=depth_model_path, DEVICE=
|
| 39 |
|
| 40 |
-
safety_detection_path =
|
| 41 |
app.state.safety_detection_model = YOLO_Detector(safety_detection_path)
|
| 42 |
|
| 43 |
-
|
| 44 |
-
app.state.settings = settings
|
| 45 |
-
# app.state.camera_metadata = {}
|
| 46 |
-
# app.state.dashboard_clients = set()
|
| 47 |
-
# Redis(host="localhost", port=6379, db=0, decode_responses=True)
|
| 48 |
app.state.redis = aioredis.from_url(settings.redis_url, decode_responses=True)
|
| 49 |
-
#
|
| 50 |
-
# Thinking of moving this to the health check.
|
| 51 |
try:
|
| 52 |
await app.state.redis.ping()
|
| 53 |
logger.info("Redis connected successfully...")
|
|
@@ -55,15 +52,14 @@ async def lifespan(app: FastAPI):
|
|
| 55 |
logger.error(f"Failed to connect to Redis: {e}")
|
| 56 |
raise e
|
| 57 |
|
| 58 |
-
# Each camera should have its tracker to be able to work fine.
|
| 59 |
-
# app.state.camera_trackers = {}
|
| 60 |
yield
|
| 61 |
|
|
|
|
| 62 |
logger.warn("Shutting down the server....")
|
| 63 |
torch.cuda.empty_cache()
|
| 64 |
await app.state.redis.close()
|
| 65 |
-
# You can remove connections and release gpu here .
|
| 66 |
|
|
|
|
| 67 |
dagshub.init(repo_owner='eslam760000', repo_name='p-tracking_system', mlflow=True)
|
| 68 |
mlflow.set_tracking_uri("sqlite:///config/logs/mlflow.db")
|
| 69 |
mlflow.set_experiment("realtime-detection-system")
|
|
@@ -76,7 +72,6 @@ app = FastAPI(
|
|
| 76 |
lifespan=lifespan
|
| 77 |
)
|
| 78 |
|
| 79 |
-
# Allow
|
| 80 |
app.add_middleware(
|
| 81 |
TrustedHostMiddleware,
|
| 82 |
allowed_hosts=["*"]
|
|
|
|
| 1 |
+
from ai.utils.hugging_face import hf_fetch_model
|
|
|
|
| 2 |
from ai.depth.depth_anything import DepthAnything
|
| 3 |
from ai.detectors.yolo_detector import YOLO_Detector
|
| 4 |
from config.settings import AppConfig
|
|
|
|
| 7 |
from api.routers import camera_stream
|
| 8 |
from api.routers import dashboard_stream
|
| 9 |
from api.routers import health
|
|
|
|
| 10 |
from infra.logger_structlog import StructLogger
|
| 11 |
+
|
| 12 |
+
from contextlib import asynccontextmanager
|
| 13 |
import mlflow
|
| 14 |
import torch
|
|
|
|
| 15 |
import redis.asyncio as aioredis
|
| 16 |
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
| 17 |
import dagshub
|
| 18 |
+
from fastapi import FastAPI
|
| 19 |
+
|
| 20 |
|
| 21 |
@asynccontextmanager
|
| 22 |
async def lifespan(app: FastAPI):
|
|
|
|
| 26 |
|
| 27 |
settings = AppConfig()
|
| 28 |
logger = StructLogger(settings=settings)
|
| 29 |
+
# Using this way to can store data. it is acts as a dict which holds instances
|
| 30 |
+
app.state.logger = logger
|
| 31 |
+
app.state.settings = settings
|
| 32 |
|
| 33 |
logger.info("Starting Server.... ")
|
| 34 |
+
# asyncio.create_task(log_system_metrics(logger, logger_interval_sec=settings.intervals.system_metrics_seconds))
|
| 35 |
|
| 36 |
+
detection_model_path = hf_fetch_model(repo_id="Ultralytics/YOLO26", filename=settings.yolo.model_name)
|
|
|
|
| 37 |
app.state.detection_model = YOLO_Detector(detection_model_path)
|
| 38 |
|
| 39 |
+
depth_model_path = hf_fetch_model(repo_id="depth-anything/Depth-Anything-V2-Small", filename=settings.depth.model_name)
|
| 40 |
+
app.state.depth_model = DepthAnything(encoder=settings.depth.encoder, depth_model_path=depth_model_path, DEVICE=settings.depth.device)
|
| 41 |
|
| 42 |
+
safety_detection_path = hf_fetch_model(repo_id="e1250/safety_detection", filename=settings.security_detector.model_name)
|
| 43 |
app.state.safety_detection_model = YOLO_Detector(safety_detection_path)
|
| 44 |
|
| 45 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
app.state.redis = aioredis.from_url(settings.redis_url, decode_responses=True)
|
| 47 |
+
# Checking connection to redis - TODO add to health check
|
|
|
|
| 48 |
try:
|
| 49 |
await app.state.redis.ping()
|
| 50 |
logger.info("Redis connected successfully...")
|
|
|
|
| 52 |
logger.error(f"Failed to connect to Redis: {e}")
|
| 53 |
raise e
|
| 54 |
|
|
|
|
|
|
|
| 55 |
yield
|
| 56 |
|
| 57 |
+
# Here You remove connections and release gpu here..
|
| 58 |
logger.warn("Shutting down the server....")
|
| 59 |
torch.cuda.empty_cache()
|
| 60 |
await app.state.redis.close()
|
|
|
|
| 61 |
|
| 62 |
+
# MLFlow setup
|
| 63 |
dagshub.init(repo_owner='eslam760000', repo_name='p-tracking_system', mlflow=True)
|
| 64 |
mlflow.set_tracking_uri("sqlite:///config/logs/mlflow.db")
|
| 65 |
mlflow.set_experiment("realtime-detection-system")
|
|
|
|
| 72 |
lifespan=lifespan
|
| 73 |
)
|
| 74 |
|
|
|
|
| 75 |
app.add_middleware(
|
| 76 |
TrustedHostMiddleware,
|
| 77 |
allowed_hosts=["*"]
|
requirements.txt
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
|
|
| 1 |
fastapi
|
| 2 |
uvicorn
|
| 3 |
gunicorn
|
| 4 |
-
websockets
|
| 5 |
|
|
|
|
| 6 |
ultralytics
|
|
|
|
| 7 |
|
|
|
|
| 8 |
pydantic
|
| 9 |
pydantic_settings
|
| 10 |
structlog
|
| 11 |
|
| 12 |
-
tracking_system@git+https://github.com/E1250/p-tracking_system.git@main
|
| 13 |
|
| 14 |
redis
|
| 15 |
upstash_redis
|
|
|
|
|
|
|
| 16 |
prometheus_client
|
|
|
|
|
|
|
| 17 |
|
| 18 |
numpy
|
| 19 |
pandas
|
| 20 |
-
|
| 21 |
-
huggingface_hub
|
| 22 |
-
dagshub
|
|
|
|
| 1 |
+
# Web
|
| 2 |
fastapi
|
| 3 |
uvicorn
|
| 4 |
gunicorn
|
| 5 |
+
websockets
|
| 6 |
|
| 7 |
+
# AI
|
| 8 |
ultralytics
|
| 9 |
+
tracking_system@git+https://github.com/E1250/p-tracking_system.git@main
|
| 10 |
|
| 11 |
+
# Arch
|
| 12 |
pydantic
|
| 13 |
pydantic_settings
|
| 14 |
structlog
|
| 15 |
|
|
|
|
| 16 |
|
| 17 |
redis
|
| 18 |
upstash_redis
|
| 19 |
+
|
| 20 |
+
# Logging & Monitoring
|
| 21 |
prometheus_client
|
| 22 |
+
dagshub
|
| 23 |
+
mlflow
|
| 24 |
|
| 25 |
numpy
|
| 26 |
pandas
|
| 27 |
+
huggingface_hub
|
|
|
|
|
|
utils/__pycache__/experiment.cpython-311.pyc
DELETED
|
Binary file (1.22 kB)
|
|
|
utils/experiment.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
|
|
| 1 |
import mlflow
|
| 2 |
|
| 3 |
-
|
| 4 |
-
return mlflow.start_run(run_name=f"camera_{camera_id}")
|
| 5 |
-
|
| 6 |
def log_config():
|
| 7 |
-
mlflow.log_param("
|
| 8 |
-
mlflow.log_param("
|
| 9 |
-
mlflow.log_param("
|
| 10 |
|
| 11 |
def log_metrics(metrics:dict):
|
| 12 |
for k, v in metrics.items():
|
|
|
|
| 1 |
+
from backend.config.settings import AppConfig
|
| 2 |
import mlflow
|
| 3 |
|
| 4 |
+
config = AppConfig()
|
|
|
|
|
|
|
| 5 |
def log_config():
|
| 6 |
+
mlflow.log_param("Detector", config.yolo.model_name)
|
| 7 |
+
mlflow.log_param("Safety Model", config.security_detector.model_name)
|
| 8 |
+
mlflow.log_param("Depth Model", config.depth.model_name)
|
| 9 |
|
| 10 |
def log_metrics(metrics:dict):
|
| 11 |
for k, v in metrics.items():
|