This commit is contained in:
HuangHai
2026-01-12 20:11:18 +08:00
parent f4b398227e
commit ac79e44282
18 changed files with 155 additions and 85 deletions

View File

@@ -7,6 +7,7 @@ import json
import aiohttp
import logging
import base64
import cv2
from openai import AsyncOpenAI, BadRequestError
from Config.Config import (
ALY_LLM_API_KEY, VL_MODEL_NAME, VL_MODEL_NAME_AD
@@ -15,6 +16,8 @@ from Apps.AiTeJiYiChong.Config.Setting import (
SAFE_EXCLUDE_RATIO, FALLBACK_WIDTH, FALLBACK_HEIGHT,
BOTTOM_SAFE_EXCLUDE_RATIO
)
from Util.PaddleOCRKit import get_ocr_kit
from Apps.AiTeJiYiChong import Kit
logger = logging.getLogger(__name__)
@@ -287,6 +290,65 @@ class ReadImageKit:
logger.error(f"Error calling VL model for detail: {e}")
return None
@classmethod
async def get_stations_hybrid(cls, image_path: str, device_info=None):
"""
混合识别模式:图形学切片 + 本地 PaddleOCR 识别
"""
if not os.path.exists(image_path):
logger.error(f"Image not found: {image_path}")
return []
# 1. 使用 Kit 中的图形学算法识别卡片区域
# Kit.crop_cards_from_image 会生成 .json, _flag.jpg, _vl.jpg
# 我们主要需要它返回的 json_data
json_data = Kit.crop_cards_from_image(image_path, save_debug=True)
if not json_data or not json_data.get("cards"):
logger.warning("No cards detected by graphical slicing.")
return []
img = Image.open(image_path).convert("RGB")
ocr_kit = get_ocr_kit()
final_stations = []
# 2. 对每个卡片区域进行 OCR 识别
# 注意:PaddleOCR 识别过程较快,且通常不涉及网络请求,可以根据需要选择并行或串行
# 这里使用串行以保证日志输出整齐,如果追求极致性能可改用 asyncio.to_thread 并行
for card in json_data["cards"]:
rect = card["rect"] # [x1, y1, x2, y2]
# 裁剪卡片
patch = img.crop((rect[0], rect[1], rect[2], rect[3]))
# 转换为 ndarray 供 PaddleOCR 使用
patch_cv = cv2.cvtColor(np.array(patch), cv2.COLOR_RGB2BGR)
# OCR 识别
logger.info(f"正在识别卡片 {card['id']}: {rect}")
res = ocr_kit.recognize(patch_cv)
if res and res.get("station_name"):
# 注入点击坐标和原始区域信息
res["uia_center_x"] = card["click_point"][0]
res["uia_center_y"] = card["click_point"][1]
res["rect"] = rect
# 转换 bounds 到 0-1000 空间(保持与 VL 模式兼容)
w, h = img.size
res["bounds"] = [
int(rect[0] * 1000 / w),
int(rect[1] * 1000 / h),
int(rect[2] * 1000 / w),
int(rect[3] * 1000 / h)
]
final_stations.append(res)
logger.info(f"卡片 {card['id']} 识别成功: {res['station_name']}")
else:
logger.warning(f"卡片 {card['id']} 识别失败或无名称")
return final_stations
@classmethod
async def get_stations_from_image(cls, image_path: str, device_info=None):
"""