Spaces:
Running
Running
restart/ crash/ tracker/ loading with minor fixes..
Browse files- backend/engine.py +10 -3
- backend/model.py +38 -14
- backend/server.py +18 -4
- requirements.txt +1 -0
backend/engine.py
CHANGED
|
@@ -90,8 +90,9 @@ def run(model, video_path, line, config, on_frame, save_annotated=False, annotat
|
|
| 90 |
annotated_dir = os.path.join(tempfile.gettempdir(), "funky_reports")
|
| 91 |
os.makedirs(annotated_dir, exist_ok=True)
|
| 92 |
annotated_path = os.path.join(annotated_dir, f"annotated_{os.path.basename(video_path)}.mp4")
|
|
|
|
| 93 |
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
| 94 |
-
writer = cv2.VideoWriter(annotated_path, fourcc,
|
| 95 |
|
| 96 |
prev_side = {}
|
| 97 |
counted_ids = set()
|
|
@@ -144,6 +145,11 @@ def run(model, video_path, line, config, on_frame, save_annotated=False, annotat
|
|
| 144 |
|
| 145 |
current = _side((cx, cy), a, b)
|
| 146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
if obj_id in prev_side and obj_id not in counted_ids:
|
| 148 |
if prev_side[obj_id] != current:
|
| 149 |
dist = _point_to_segment_dist(cx, cy, a[0], a[1], b[0], b[1])
|
|
@@ -160,10 +166,11 @@ def run(model, video_path, line, config, on_frame, save_annotated=False, annotat
|
|
| 160 |
|
| 161 |
prev_side[obj_id] = current
|
| 162 |
|
| 163 |
-
# Write annotated frame
|
|
|
|
| 164 |
if writer is not None:
|
| 165 |
frame = r.orig_img.copy()
|
| 166 |
-
_draw_annotations(frame, cur_boxes, cur_ids,
|
| 167 |
writer.write(frame)
|
| 168 |
|
| 169 |
congestion.append(active)
|
|
|
|
| 90 |
annotated_dir = os.path.join(tempfile.gettempdir(), "funky_reports")
|
| 91 |
os.makedirs(annotated_dir, exist_ok=True)
|
| 92 |
annotated_path = os.path.join(annotated_dir, f"annotated_{os.path.basename(video_path)}.mp4")
|
| 93 |
+
writer_fps = max(1.0, fps / stride)
|
| 94 |
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
| 95 |
+
writer = cv2.VideoWriter(annotated_path, fourcc, writer_fps, (out_w, out_h))
|
| 96 |
|
| 97 |
prev_side = {}
|
| 98 |
counted_ids = set()
|
|
|
|
| 145 |
|
| 146 |
current = _side((cx, cy), a, b)
|
| 147 |
|
| 148 |
+
# Skip if centroid is exactly on the line (cross-product == 0)
|
| 149 |
+
# — avoids misfired crossings due to floating-point boundary hits
|
| 150 |
+
if current == 0:
|
| 151 |
+
continue
|
| 152 |
+
|
| 153 |
if obj_id in prev_side and obj_id not in counted_ids:
|
| 154 |
if prev_side[obj_id] != current:
|
| 155 |
dist = _point_to_segment_dist(cx, cy, a[0], a[1], b[0], b[1])
|
|
|
|
| 166 |
|
| 167 |
prev_side[obj_id] = current
|
| 168 |
|
| 169 |
+
# Write annotated frame
|
| 170 |
+
cur_clses = cls if r.boxes.id is not None else None
|
| 171 |
if writer is not None:
|
| 172 |
frame = r.orig_img.copy()
|
| 173 |
+
_draw_annotations(frame, cur_boxes, cur_ids, cur_clses, [a, b], annotated_options)
|
| 174 |
writer.write(frame)
|
| 175 |
|
| 176 |
congestion.append(active)
|
backend/model.py
CHANGED
|
@@ -5,27 +5,51 @@ from ultralytics import YOLO
|
|
| 5 |
|
| 6 |
load_dotenv()
|
| 7 |
|
|
|
|
|
|
|
|
|
|
| 8 |
MODEL_DIR = Path(__file__).parent / "weights"
|
| 9 |
-
PT_PATH = MODEL_DIR / "best.pt"
|
| 10 |
OV_DIR = MODEL_DIR / "best_int8_openvino_model"
|
| 11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
def
|
| 14 |
-
|
|
|
|
| 15 |
|
| 16 |
-
if not PT_PATH.exists():
|
| 17 |
-
token = os.getenv("HF_TOKEN")
|
| 18 |
-
os.system(
|
| 19 |
-
f'curl -L -H "Authorization: Bearer {token}" '
|
| 20 |
-
f'-o {PT_PATH} '
|
| 21 |
-
f'https://huggingface.co/Perception365/VehicleNet-Y26s/resolve/main/weights/best.pt'
|
| 22 |
-
)
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
|
| 29 |
def load_model():
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
return YOLO(str(OV_DIR), task="detect")
|
|
|
|
| 5 |
|
| 6 |
load_dotenv()
|
| 7 |
|
| 8 |
+
# Silence Ultralytics config dir warning on read-only containers
|
| 9 |
+
os.environ.setdefault("YOLO_CONFIG_DIR", "/tmp/Ultralytics")
|
| 10 |
+
|
| 11 |
MODEL_DIR = Path(__file__).parent / "weights"
|
|
|
|
| 12 |
OV_DIR = MODEL_DIR / "best_int8_openvino_model"
|
| 13 |
|
| 14 |
+
_HF_BASE = (
|
| 15 |
+
"https://huggingface.co/Perception365/VehicleNet-Y26s"
|
| 16 |
+
"/resolve/main/weights/best_int8_openvino_model"
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
# The three files that make up the OpenVINO INT8 model
|
| 20 |
+
_OV_FILES = ["best.bin", "best.xml", "metadata.yaml"]
|
| 21 |
+
|
| 22 |
|
| 23 |
+
def _auth_header():
|
| 24 |
+
token = os.getenv("HF_TOKEN", "")
|
| 25 |
+
return f'-H "Authorization: Bearer {token}"' if token else ""
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
+
def _download_ov_model():
|
| 29 |
+
"""Download the pre-built OV INT8 model files directly from HF."""
|
| 30 |
+
OV_DIR.mkdir(parents=True, exist_ok=True)
|
| 31 |
+
auth = _auth_header()
|
| 32 |
+
for filename in _OV_FILES:
|
| 33 |
+
dest = OV_DIR / filename
|
| 34 |
+
if dest.exists():
|
| 35 |
+
print(f"[model] {filename} already present, skipping.")
|
| 36 |
+
continue
|
| 37 |
+
url = f"{_HF_BASE}/{filename}"
|
| 38 |
+
print(f"[model] Downloading {filename} ...")
|
| 39 |
+
ret = os.system(f'curl -L --fail {auth} -o "{dest}" "{url}"')
|
| 40 |
+
if ret != 0 or not dest.exists() or dest.stat().st_size < 100:
|
| 41 |
+
raise RuntimeError(
|
| 42 |
+
f"[model] Failed to download {filename} from HF. "
|
| 43 |
+
"Check HF_TOKEN and repo visibility."
|
| 44 |
+
)
|
| 45 |
+
print("[model] OV model files ready ✅")
|
| 46 |
|
| 47 |
|
| 48 |
def load_model():
|
| 49 |
+
if not OV_DIR.exists() or not all(
|
| 50 |
+
(OV_DIR / f).exists() for f in _OV_FILES
|
| 51 |
+
):
|
| 52 |
+
_download_ov_model()
|
| 53 |
+
else:
|
| 54 |
+
print("[model] OV model already present — skipping download.")
|
| 55 |
return YOLO(str(OV_DIR), task="detect")
|
backend/server.py
CHANGED
|
@@ -17,7 +17,8 @@ from engine import run
|
|
| 17 |
from constants import MODEL_CLASSES, BUSINESS_MAP
|
| 18 |
from visualize import generate_all
|
| 19 |
|
| 20 |
-
|
|
|
|
| 21 |
|
| 22 |
BASE = Path(__file__).parent.parent
|
| 23 |
FRONTEND = BASE / "frontend"
|
|
@@ -31,10 +32,23 @@ run_results = {}
|
|
| 31 |
model = None
|
| 32 |
|
| 33 |
|
| 34 |
-
@
|
| 35 |
-
def
|
| 36 |
global model
|
| 37 |
model = load_model()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
|
| 40 |
@app.get("/")
|
|
@@ -135,7 +149,7 @@ async def ws_run(ws: WebSocket):
|
|
| 135 |
|
| 136 |
path = videos.get(video_id)
|
| 137 |
|
| 138 |
-
loop = asyncio.
|
| 139 |
|
| 140 |
queue = asyncio.Queue()
|
| 141 |
|
|
|
|
| 17 |
from constants import MODEL_CLASSES, BUSINESS_MAP
|
| 18 |
from visualize import generate_all
|
| 19 |
|
| 20 |
+
from contextlib import asynccontextmanager
|
| 21 |
+
import numpy as np
|
| 22 |
|
| 23 |
BASE = Path(__file__).parent.parent
|
| 24 |
FRONTEND = BASE / "frontend"
|
|
|
|
| 32 |
model = None
|
| 33 |
|
| 34 |
|
| 35 |
+
@asynccontextmanager
|
| 36 |
+
async def lifespan(app: FastAPI):
|
| 37 |
global model
|
| 38 |
model = load_model()
|
| 39 |
+
# Warm up: run a dummy inference so OpenVINO compiles its graph now,
|
| 40 |
+
# not on the first real user request
|
| 41 |
+
try:
|
| 42 |
+
dummy = np.zeros((1, 3, 640, 640), dtype=np.uint8)
|
| 43 |
+
model(dummy, verbose=False)
|
| 44 |
+
print("[BACKEND] Model warm-up complete.")
|
| 45 |
+
except Exception as e:
|
| 46 |
+
print(f"[BACKEND] Warm-up skipped: {e}")
|
| 47 |
+
yield
|
| 48 |
+
# Shutdown: nothing to clean up
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
app = FastAPI(lifespan=lifespan)
|
| 52 |
|
| 53 |
|
| 54 |
@app.get("/")
|
|
|
|
| 149 |
|
| 150 |
path = videos.get(video_id)
|
| 151 |
|
| 152 |
+
loop = asyncio.get_running_loop()
|
| 153 |
|
| 154 |
queue = asyncio.Queue()
|
| 155 |
|
requirements.txt
CHANGED
|
@@ -9,6 +9,7 @@ python-dotenv==1.0.0
|
|
| 9 |
websockets==12.0
|
| 10 |
onnxruntime
|
| 11 |
openvino>=2024.0.0
|
|
|
|
| 12 |
onnx>=1.12.0
|
| 13 |
onnxslim>=0.1.71
|
| 14 |
lap>=0.5.12
|
|
|
|
| 9 |
websockets==12.0
|
| 10 |
onnxruntime
|
| 11 |
openvino>=2024.0.0
|
| 12 |
+
nncf>=2.14.0,<3.0.0
|
| 13 |
onnx>=1.12.0
|
| 14 |
onnxslim>=0.1.71
|
| 15 |
lap>=0.5.12
|