python-detector-worker/siwatsystem/pympta.py

143 lines
5.5 KiB
Python

import os
import json
import logging
import torch
import cv2
import requests
import zipfile
import shutil
from ultralytics import YOLO
from urllib.parse import urlparse
def load_pipeline_node(node_config: dict, mpta_dir: str) -> dict:
# Recursively load a model node from configuration.
model_path = os.path.join(mpta_dir, node_config["modelFile"])
if not os.path.exists(model_path):
logging.error(f"Model file {model_path} not found.")
raise FileNotFoundError(f"Model file {model_path} not found.")
logging.info(f"Loading model for node {node_config['modelId']} from {model_path}")
model = YOLO(model_path)
if torch.cuda.is_available():
model.to("cuda")
node = {
"modelId": node_config["modelId"],
"modelFile": node_config["modelFile"],
"triggerClasses": node_config.get("triggerClasses", []),
"crop": node_config.get("crop", False),
"minConfidence": node_config.get("minConfidence", None),
"model": model,
"branches": []
}
for child in node_config.get("branches", []):
node["branches"].append(load_pipeline_node(child, mpta_dir))
return node
def load_pipeline_from_zip(zip_source: str, target_dir: str) -> dict:
os.makedirs(target_dir, exist_ok=True)
zip_path = os.path.join(target_dir, "pipeline.mpta")
# Parse the source; only local files are supported here.
parsed = urlparse(zip_source)
if parsed.scheme in ("", "file"):
local_path = parsed.path if parsed.scheme == "file" else zip_source
if os.path.exists(local_path):
try:
shutil.copy(local_path, zip_path)
logging.info(f"Copied local .mpta file from {local_path} to {zip_path}")
except Exception as e:
logging.error(f"Failed to copy local .mpta file from {local_path}: {e}")
return None
else:
logging.error(f"Local file {local_path} does not exist.")
return None
else:
logging.error("HTTP download functionality has been moved. Use a local file path here.")
return None
try:
with zipfile.ZipFile(zip_path, "r") as zip_ref:
zip_ref.extractall(target_dir)
logging.info(f"Extracted .mpta file to {target_dir}")
except Exception as e:
logging.error(f"Failed to extract .mpta file: {e}")
return None
finally:
if os.path.exists(zip_path):
os.remove(zip_path)
pipeline_name = os.path.basename(zip_source)
pipeline_name = os.path.splitext(pipeline_name)[0]
mpta_dir = os.path.join(target_dir, pipeline_name)
pipeline_json_path = os.path.join(mpta_dir, "pipeline.json")
if not os.path.exists(pipeline_json_path):
logging.error("pipeline.json not found in the .mpta file")
return None
try:
with open(pipeline_json_path, "r") as f:
pipeline_config = json.load(f)
return load_pipeline_node(pipeline_config["pipeline"], mpta_dir)
except Exception as e:
logging.error(f"Error loading pipeline.json: {e}")
return None
def run_pipeline(frame, node: dict, return_bbox: bool = False):
"""
Processes the frame with the given pipeline node. When return_bbox is True,
the function returns a tuple (detection, bbox) where bbox is (x1,y1,x2,y2)
for drawing. Otherwise, returns only the detection.
"""
try:
results = node["model"].track(frame, stream=False, persist=True)
detection = None
best_box = None
max_conf = -1
for r in results:
for box in r.boxes:
box_cpu = box.cpu()
conf = float(box_cpu.conf[0])
if conf > max_conf and hasattr(box, "id") and box.id is not None:
max_conf = conf
detection = {
"class": node["model"].names[int(box_cpu.cls[0])],
"confidence": conf,
"id": box.id.item()
}
best_box = box_cpu
bbox = None
if detection and node.get("crop", False) and best_box is not None:
coords = best_box.xyxy[0]
x1, y1, x2, y2 = map(int, coords)
h, w = frame.shape[:2]
x1, y1 = max(0, x1), max(0, y1)
x2, y2 = min(w, x2), min(h, y2)
if x2 > x1 and y2 > y1:
bbox = (x1, y1, x2, y2)
frame = frame[y1:y2, x1:x2]
if detection is not None:
for branch in node["branches"]:
if detection["class"] in branch.get("triggerClasses", []):
min_conf = branch.get("minConfidence")
if min_conf is not None and detection["confidence"] < min_conf:
logging.debug(f"Confidence {detection['confidence']} below threshold {min_conf} for branch {branch['modelId']}.")
if return_bbox:
return detection, bbox
return detection
res = run_pipeline(frame, branch, return_bbox)
if res is not None:
if return_bbox:
return res
return res
if return_bbox:
return detection, bbox
return detection
if return_bbox:
return None, None
return None
except Exception as e:
logging.error(f"Error running pipeline on node {node.get('modelId')}: {e}")
if return_bbox:
return None, None
return None