This commit is contained in:
wanghanbo 2026-09-29 18:28:23 +08:00 committed by LiXin01
parent 9caf89e48b
commit ae1f8b9830
5 changed files with 85 additions and 15 deletions

View File

@ -16,7 +16,7 @@ class Config:
# 你的Plate检测模型路径(相对项目根目录);若存在 best_openvino_model 会自动优先加载 # 你的Plate检测模型路径(相对项目根目录);若存在 best_openvino_model 会自动优先加载
MODEL_PATH = str(_ROOT / "best.pt") MODEL_PATH = str(_ROOT / "best.pt")
CONF_THRESHOLD = 0.8 CONF_THRESHOLD = 0.90
IOU_THRESHOLD = 0.45 IOU_THRESHOLD = 0.45
# 显式 imgsz,便于 OpenVINO / CPU 控制耗时;可用环境变量 DISH_YOLO_IMGSZ 覆盖 # 显式 imgsz,便于 OpenVINO / CPU 控制耗时;可用环境变量 DISH_YOLO_IMGSZ 覆盖
IMGSZ = int(os.environ.get("DISH_YOLO_IMGSZ", "640")) IMGSZ = int(os.environ.get("DISH_YOLO_IMGSZ", "640"))

View File

@ -31,13 +31,43 @@ def format_predict_detail(detail):
def format_dish_box_label(detail): def format_dish_box_label(detail):
"""将识别结果格式化为图片框上的菜品名称标签。""" """将识别结果格式化为图片框标签。
默认只显示菜名;设环境变量 DISH_SHOW_BOX_SCORES=1 时追加「似xx 检xx」。
"""
if not detail or detail.get("error"): if not detail or detail.get("error"):
return "识别失败" return "识别失败"
if detail.get("is_unknown"): if detail.get("is_unknown"):
suggested = detail.get("suggested_category") suggested = detail.get("suggested_category")
return f"未知·{suggested}" if suggested else "未知" base = f"未知·{suggested}" if suggested else "未知"
return detail.get("label") or "未知" else:
base = detail.get("label") or detail.get("name") or "未知"
try:
from dao.settings import DISH_SHOW_BOX_SCORES
show_scores = bool(DISH_SHOW_BOX_SCORES)
except Exception:
show_scores = False
if not show_scores:
return str(base)
def _fmt(v):
try:
return f"{float(v):.2f}" if v is not None else None
except (TypeError, ValueError):
return None
sim = _fmt(detail.get("score"))
det = _fmt(detail.get("det_conf"))
bits = []
if sim is not None:
bits.append(f"似{sim}")
if det is not None:
bits.append(f"检{det}")
if not bits:
return str(base)
return f"{base} {' '.join(bits)}"
class PendingLearningUIMixin: class PendingLearningUIMixin:

View File

@ -71,6 +71,9 @@ DISH_DEVICE_STATUS_ENABLED = os.environ.get('DISH_DEVICE_STATUS_ENABLED', '1') =
DISH_SYNC_ON_START = os.environ.get('DISH_SYNC_ON_START', '1') == '1' DISH_SYNC_ON_START = os.environ.get('DISH_SYNC_ON_START', '1') == '1'
DISH_SYNC_INTERVAL = int(os.environ.get('DISH_SYNC_INTERVAL', 300)) # 默认 5 分钟 DISH_SYNC_INTERVAL = int(os.environ.get('DISH_SYNC_INTERVAL', 300)) # 默认 5 分钟
# 识别框是否显示「似xx 检xx」(默认关;需要时 set DISH_SHOW_BOX_SCORES=1)
DISH_SHOW_BOX_SCORES = os.environ.get('DISH_SHOW_BOX_SCORES', '0') == '1'
DEBUG = True DEBUG = True

View File

@ -1759,12 +1759,8 @@ class BaseDishApp(PendingLearningUIMixin):
print(f"[live] preview failed: {e}") print(f"[live] preview failed: {e}")
def _live_dish_label(self, dish): def _live_dish_label(self, dish):
if not dish: from ui.pending_learning_ui import format_dish_box_label
return "未知" return format_dish_box_label(dish)
if dish.get("is_unknown"):
sug = (dish.get("suggested_category") or "").strip()
return f"未知·{sug}" if sug else "未知"
return (dish.get("label") or dish.get("name") or "未知").strip() or "未知"
def _draw_live_overlay_pil(self, pil_im, dishes, sx=1.0, sy=1.0): def _draw_live_overlay_pil(self, pil_im, dishes, sx=1.0, sy=1.0):
"""在已缩放预览图上画识别框:整图一次 alpha 合成,避免逐框卡顿。""" """在已缩放预览图上画识别框:整图一次 alpha 合成,避免逐框卡顿。"""
@ -1967,6 +1963,7 @@ class BaseDishApp(PendingLearningUIMixin):
boxes, boxes,
auto_pending=False, auto_pending=False,
crop_images=crop_arrays, crop_images=crop_arrays,
det_confs=[c.get("conf") for c in crop_items],
) )
self._last_detect_fp = fp self._last_detect_fp = fp
self._last_live_dishes = list(dishes or []) self._last_live_dishes = list(dishes or [])
@ -2404,6 +2401,7 @@ class BaseDishApp(PendingLearningUIMixin):
f"正在识别第 {cur} 张…", "busy" f"正在识别第 {cur} 张…", "busy"
), ),
crop_images=crop_arrays, crop_images=crop_arrays,
det_confs=[c.get("conf") for c in crop_items],
) )
preview = labeled_path or visualize_save_path preview = labeled_path or visualize_save_path
if preview: if preview:
@ -2523,7 +2521,9 @@ class BaseDishApp(PendingLearningUIMixin):
return return
final_result, _, _unknown_count, labeled_path, dishes = self.run_plate_predict_batch( final_result, _, _unknown_count, labeled_path, dishes = self.run_plate_predict_batch(
save_path, cropped_file_paths, boxes, crop_images=crop_arrays, save_path, cropped_file_paths, boxes,
crop_images=crop_arrays,
det_confs=[c.get("conf") for c in crop_items],
) )
preview = labeled_path or visualize_save_path preview = labeled_path or visualize_save_path
self.report_recognition(save_path, dishes, preview) self.report_recognition(save_path, dishes, preview)

View File

@ -31,13 +31,43 @@ def format_predict_detail(detail):
def format_dish_box_label(detail): def format_dish_box_label(detail):
"""将识别结果格式化为图片框上的菜品名称标签。""" """将识别结果格式化为图片框标签。
默认只显示菜名;设环境变量 DISH_SHOW_BOX_SCORES=1 时追加「似xx 检xx」。
"""
if not detail or detail.get("error"): if not detail or detail.get("error"):
return "识别失败" return "识别失败"
if detail.get("is_unknown"): if detail.get("is_unknown"):
suggested = detail.get("suggested_category") suggested = detail.get("suggested_category")
return f"未知·{suggested}" if suggested else "未知" base = f"未知·{suggested}" if suggested else "未知"
return detail.get("label") or "未知" else:
base = detail.get("label") or detail.get("name") or "未知"
try:
from dao.settings import DISH_SHOW_BOX_SCORES
show_scores = bool(DISH_SHOW_BOX_SCORES)
except Exception:
show_scores = False
if not show_scores:
return str(base)
def _fmt(v):
try:
return f"{float(v):.2f}" if v is not None else None
except (TypeError, ValueError):
return None
sim = _fmt(detail.get("score"))
det = _fmt(detail.get("det_conf"))
bits = []
if sim is not None:
bits.append(f"似{sim}")
if det is not None:
bits.append(f"检{det}")
if not bits:
return str(base)
return f"{base} {' '.join(bits)}"
class PendingLearningUIMixin: class PendingLearningUIMixin:
@ -689,6 +719,7 @@ class PendingLearningUIMixin:
def run_plate_predict_batch( def run_plate_predict_batch(
self, source_image_path, cropped_file_paths, boxes, self, source_image_path, cropped_file_paths, boxes,
progress_callback=None, auto_pending=True, crop_images=None, progress_callback=None, auto_pending=True, crop_images=None,
det_confs=None,
): ):
"""对多个裁剪区域识别。 """对多个裁剪区域识别。
@ -698,6 +729,7 @@ class PendingLearningUIMixin:
cropped_file_paths: 裁剪图路径列表(可与 crop_images 二选一/并用) cropped_file_paths: 裁剪图路径列表(可与 crop_images 二选一/并用)
crop_images: 可选 BGR ndarray 列表,优先用于特征提取(免读盘) crop_images: 可选 BGR ndarray 列表,优先用于特征提取(免读盘)
det_confs: 可选 YOLO 检测置信度列表(与 boxes 对齐)
返回: 返回:
final_result, dish_labels, unknown_count, labeled_path, dishes final_result, dish_labels, unknown_count, labeled_path, dishes
@ -728,6 +760,7 @@ class PendingLearningUIMixin:
if progress_callback: if progress_callback:
progress_callback(i + 1, n) progress_callback(i + 1, n)
box = boxes[i] if boxes and i < len(boxes) else None box = boxes[i] if boxes and i < len(boxes) else None
det_conf = det_confs[i] if det_confs and i < len(det_confs) else None
img_path = cropped_file_paths[i] if i < len(cropped_file_paths) else None img_path = cropped_file_paths[i] if i < len(cropped_file_paths) else None
try: try:
detail = self.manager.predict_from_feature( detail = self.manager.predict_from_feature(
@ -737,12 +770,15 @@ class PendingLearningUIMixin:
) )
if detail.get("is_unknown"): if detail.get("is_unknown"):
unknown_count += 1 unknown_count += 1
dish_labels.append(format_dish_box_label(detail)) label_src = dict(detail)
label_src["det_conf"] = det_conf
dish_labels.append(format_dish_box_label(label_src))
final_result += f"【菜品{i + 1}】{format_predict_detail(detail)}\n" final_result += f"【菜品{i + 1}】{format_predict_detail(detail)}\n"
dishes.append({ dishes.append({
"index": i + 1, "index": i + 1,
"label": detail.get("label"), "label": detail.get("label"),
"score": detail.get("score"), "score": detail.get("score"),
"det_conf": det_conf,
"is_unknown": bool(detail.get("is_unknown")), "is_unknown": bool(detail.get("is_unknown")),
"suggested_category": detail.get("suggested_category"), "suggested_category": detail.get("suggested_category"),
"pending_id": detail.get("pending_id"), "pending_id": detail.get("pending_id"),
@ -759,6 +795,7 @@ class PendingLearningUIMixin:
"index": i + 1, "index": i + 1,
"label": None, "label": None,
"score": None, "score": None,
"det_conf": det_conf,
"is_unknown": True, "is_unknown": True,
"suggested_category": None, "suggested_category": None,
"pending_id": None, "pending_id": None,