feat: 配置写死与 baked 模块,Alembic 建表,百度仅 BAIDU_*
- 新增 app/baked/algorithm|pipeline,非部署参数不再走 env;Settings 保留 DB/HTTP/RTSP/海康/百度/MinIO/Demo - 移除 init_db_schema 与 reload 配置;main 仅 check_database;start*.sh 在 uvicorn 前执行 alembic upgrade head - 依赖 psycopg[binary] 供 Alembic 同步 URL;alembic/env 注释与预发清单更新 - 撕段门控消费管线、各视频/语音/归档调用改为 baked - 百度环境变量仅 BAIDU_APP_ID、BAIDU_API_KEY、BAIDU_SECRET_KEY 与 BAIDU_* 超时/ASR;人脸脚本与 baidu_speech 文案同步 - 全量单测与 .env.example 更新;.gitignore 忽略 refs/(本地权重/视频不入库) Made-with: Cursor
This commit is contained in:
@@ -19,7 +19,7 @@ import yaml
|
||||
from loguru import logger
|
||||
from ultralytics import YOLO
|
||||
|
||||
from app.config import Settings, settings
|
||||
from app.baked import algorithm as ba
|
||||
|
||||
|
||||
def _ensure_yolo_config_dir() -> None:
|
||||
@@ -361,14 +361,20 @@ def window_bucket_to_best_snap(
|
||||
class ConsumableVisionAlgorithmService:
|
||||
"""手部检测(可选)+ 耗材分类;供 CameraSessionManager 在视频线程中调用。"""
|
||||
|
||||
def __init__(self, app_settings: Settings | None = None) -> None:
|
||||
def __init__(self, *, labels_yaml_path: str | None = None) -> None:
|
||||
_ensure_yolo_config_dir()
|
||||
self._s = app_settings or settings
|
||||
self._labels_yaml_path = labels_yaml_path
|
||||
self._det: YOLO | None = None
|
||||
self._cls: YOLO | None = None
|
||||
self._det_lock = Lock()
|
||||
self._cls_lock = Lock()
|
||||
|
||||
def _labels_path(self) -> Path:
|
||||
raw = self._labels_yaml_path
|
||||
if raw is not None and str(raw).strip():
|
||||
return Path(str(raw).strip()).expanduser()
|
||||
return Path(ba.CONSUMABLE_CLASSIFIER_LABELS_YAML_PATH).expanduser()
|
||||
|
||||
def effective_candidate_consumables(self, requested: list[str]) -> list[str]:
|
||||
"""请求体中的耗材子集;未提供(缺省或仅空白)时先用 ``consumable_classifier_labels.yaml`` 的 ``names``,无有效 YAML 则分类模型类名。"""
|
||||
out: list[str] = []
|
||||
@@ -382,7 +388,7 @@ class ConsumableVisionAlgorithmService:
|
||||
if out:
|
||||
return out
|
||||
|
||||
yaml_path = Path(self._s.consumable_classifier_labels_yaml_path).expanduser()
|
||||
yaml_path = self._labels_path()
|
||||
if yaml_path.is_file():
|
||||
ylist = list_sorted_class_names_from_yaml(yaml_path)
|
||||
if ylist:
|
||||
@@ -404,7 +410,7 @@ class ConsumableVisionAlgorithmService:
|
||||
if not candidates_norm:
|
||||
return {}
|
||||
|
||||
yaml_path = Path(self._s.consumable_classifier_labels_yaml_path).expanduser()
|
||||
yaml_path = self._labels_path()
|
||||
yaml_map: dict[str, str] = {}
|
||||
if yaml_path.is_file():
|
||||
try:
|
||||
@@ -420,19 +426,14 @@ class ConsumableVisionAlgorithmService:
|
||||
return out
|
||||
|
||||
def _det_weights(self) -> Path | None:
|
||||
raw = (self._s.hand_detection_weights or "").strip()
|
||||
raw = (ba.HAND_DETECTION_WEIGHTS or "").strip()
|
||||
if not raw:
|
||||
return None
|
||||
p = Path(raw).expanduser()
|
||||
return p if p.is_file() else None
|
||||
|
||||
def _cls_weights(self) -> Path:
|
||||
raw = (self._s.consumable_classifier_weights or "").strip()
|
||||
if not raw:
|
||||
raise ModelNotConfiguredError(
|
||||
"未配置耗材分类权重。请设置 CONSUMABLE_CLASSIFIER_WEIGHTS。"
|
||||
)
|
||||
p = Path(raw).expanduser().resolve()
|
||||
p = Path(ba.CONSUMABLE_CLASSIFIER_WEIGHTS).expanduser().resolve()
|
||||
if not p.is_file():
|
||||
raise ModelNotConfiguredError(f"耗材分类权重不存在: {p}")
|
||||
return p
|
||||
@@ -468,7 +469,7 @@ class ConsumableVisionAlgorithmService:
|
||||
imgsz_det: int,
|
||||
) -> np.ndarray | None:
|
||||
h, w = frame.shape[:2]
|
||||
device = resolve_inference_device(self._s.hand_detection_device)
|
||||
device = resolve_inference_device(ba.HAND_DETECTION_DEVICE)
|
||||
results = det_model.predict(
|
||||
frame,
|
||||
conf=det_conf,
|
||||
@@ -499,28 +500,28 @@ class ConsumableVisionAlgorithmService:
|
||||
crop = self.hand_crop(
|
||||
frame,
|
||||
det_model,
|
||||
det_conf=self._s.hand_detection_conf,
|
||||
pad_ratio=self._s.hand_detection_pad_ratio,
|
||||
min_crop_px=self._s.hand_detection_min_crop_px,
|
||||
imgsz_det=self._s.hand_detection_imgsz,
|
||||
det_conf=ba.HAND_DETECTION_CONF,
|
||||
pad_ratio=ba.HAND_DETECTION_PAD_RATIO,
|
||||
min_crop_px=ba.HAND_DETECTION_MIN_CROP_PX,
|
||||
imgsz_det=ba.HAND_DETECTION_IMGSZ,
|
||||
)
|
||||
if crop is None:
|
||||
return None
|
||||
else:
|
||||
crop = frame
|
||||
|
||||
device = resolve_inference_device(self._s.consumable_classifier_device)
|
||||
device = resolve_inference_device(ba.CONSUMABLE_CLASSIFIER_DEVICE)
|
||||
try:
|
||||
r = cls_model.predict(
|
||||
crop,
|
||||
imgsz=self._s.consumable_classifier_imgsz,
|
||||
imgsz=ba.CONSUMABLE_CLASSIFIER_IMGSZ,
|
||||
device=device,
|
||||
verbose=False,
|
||||
)
|
||||
except Exception as exc:
|
||||
raise PredictionError(f"耗材分类推理失败: {exc}") from exc
|
||||
|
||||
yp = Path(self._s.consumable_classifier_labels_yaml_path).expanduser()
|
||||
yp = self._labels_path()
|
||||
if yp.is_file():
|
||||
st = yp.stat()
|
||||
index_to_label_id = _cached_index_to_label_id(
|
||||
@@ -537,7 +538,7 @@ class ConsumableVisionAlgorithmService:
|
||||
)
|
||||
if snap is None:
|
||||
return None
|
||||
if snap.t1_conf < self._s.consumable_min_cls_confidence:
|
||||
if snap.t1_conf < ba.CONSUMABLE_MIN_CLS_CONFIDENCE:
|
||||
return None
|
||||
pname = snap.t1_name
|
||||
if not pname:
|
||||
|
||||
Reference in New Issue
Block a user