This commit is contained in:
HuangHai
2026-01-13 20:54:49 +08:00
parent 6a11893d07
commit ddb63403b7
7 changed files with 168 additions and 45 deletions

View File

@@ -12,8 +12,8 @@ from Util.VLMKit import VLMKit
import json
import re
from Apps.YeLiTe.Kit import draw_rectangles
from Apps.YeLiTe.Config.Setting import DRAW_DEBUG_BOXES
from Apps.YeLiTe.Kit import draw_rectangles, detect_cards_cv
from Apps.YeLiTe.Config.Setting import DRAW_DEBUG_BOXES, SAFE_EXCLUDE_RATIO, BOTTOM_SAFE_EXCLUDE_RATIO
logger = logging.getLogger(__name__)
@@ -57,43 +57,61 @@ class ReadImageKit:
async def analyze_station_list(self, image_path):
"""
分析场站列表页图片,提取场站位置和基本信息
分析场站列表页图片,提取场站位置和基本信息 (Hybrid 模式: CV 检测边界 + VLM 识别文字)
"""
prompt = """
分析这张充电站列表截图,提取所有充电站卡片信息。
# 1. 使用图形学检测卡片边界
cv_bboxes = detect_cards_cv(image_path, top_ratio=SAFE_EXCLUDE_RATIO, bottom_ratio=BOTTOM_SAFE_EXCLUDE_RATIO)
**重要过滤规则:**
1. 忽略顶部的筛选栏、排序方式区域(包含“排序方式”、“离我最近”、“地图模式”、“停车收费”、“限时免费”等按钮的区域)。
2. 仅提取下方重复出现的场站信息卡片。
3. 场站卡片通常包含:场站名称(如“驿来特...”)、价格(如“¥1.33/度”)、快/慢充桩数量、距离等。
输出格式为 JSON 数组,每个对象包含:
- "name": 场站名称
- "point": 场站卡片的中心点击坐标 [x, y]
- "bbox": 场站卡片的边界框 [x1, y1, x2, y2]
注意:
1. 仅提取明显的场站列表卡片。
2. 坐标请以像素为单位。
"""
if cv_bboxes:
# 在图片上绘制绿色框,方便 VLM 对应
draw_rectangles(image_path, cv_bboxes)
prompt = f"""
图片中已经用绿色矩形框标记了 {len(cv_bboxes)} 个可能的充电站卡片。
请按从上到下的顺序,识别每个绿色框内的场站信息。
输出格式为 JSON 数组,长度必须为 {len(cv_bboxes)}。
如果某个框内不是有效的场站卡片,请在对应的数组位置返回 null。
每个对象包含:
- "name": 场站名称 (仅提取名称文字)
- "is_valid": true/false (是否为真实的场站卡片)
注意:仅提取名称,不要包含距离或价格。
"""
else:
# 如果 CV 没检测到,退回到纯 VLM 模式
prompt = """
分析这张充电站列表截图,提取所有充电站卡片信息。
忽略顶部的筛选栏,仅提取下方重复出现的场站卡片。
输出格式为 JSON 数组,每个对象包含:
- "name": 场站名称
- "point": 场站卡片的中心点击坐标 [x, y]
- "bbox": 场站卡片的边界框 [x1, y1, x2, y2]
"""
try:
res_text = await self.vlm.analyze_image(image_path, prompt)
# 使用 VLMKit 的提取方法
json_str = self.vlm.extract_json(res_text)
stations = json.loads(json_str)
vlm_results = json.loads(json_str)
if isinstance(stations, list):
# 调试绘图
if DRAW_DEBUG_BOXES:
bboxes = [s['bbox'] for s in stations if 'bbox' in s]
points = [s['point'] for s in stations if 'point' in s]
draw_rectangles(image_path, bboxes + points)
final_stations = []
if cv_bboxes and isinstance(vlm_results, list):
# 将 VLM 识别到的名称与 CV 的坐标匹配
for i, res in enumerate(vlm_results):
if i < len(cv_bboxes) and res and res.get("is_valid"):
bbox = cv_bboxes[i]
final_stations.append({
"name": res.get("name"),
"point": [(bbox[0] + bbox[2]) // 2, (bbox[1] + bbox[3]) // 2],
"bbox": bbox
})
elif not cv_bboxes:
final_stations = vlm_results if isinstance(vlm_results, list) else []
return stations
return []
return final_stations
except Exception as e:
logger.error(f"VLM 分析列表页失败: {e}")
logger.error(f"Hybrid 分析列表页失败: {e}")
return []
async def analyze_detail_price(self, image_path):