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 会自动优先加载
MODEL_PATH = str(_ROOT / "best.pt")
CONF_THRESHOLD = 0.8
CONF_THRESHOLD = 0.90
IOU_THRESHOLD = 0.45
# 显式 imgsz,便于 OpenVINO / CPU 控制耗时;可用环境变量 DISH_YOLO_IMGSZ 覆盖
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):
"""将识别结果格式化为图片框上的菜品名称标签。"""
"""将识别结果格式化为图片框标签。
默认只显示菜名;设环境变量 DISH_SHOW_BOX_SCORES=1 时追加「似xx 检xx」。
"""
if not detail or detail.get("error"):
return "识别失败"
if detail.get("is_unknown"):
suggested = detail.get("suggested_category")
return f"未知·{suggested}" if suggested else "未知"
return detail.get("label") or "未知"
base = f"未知·{suggested}" if suggested else "未知"
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:

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_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

View File

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

View File

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