This commit is contained in:
HuangHai
2026-01-25 14:40:30 +08:00
parent b4d0e56037
commit 35654ec166
10 changed files with 181 additions and 28 deletions

View File

@@ -82,6 +82,7 @@ async def get_vlm_analysis(image_path):
【核心任务】
识别图中的【语音消息气泡】和【文本消息气泡】,并区分【发送者】。
⚠️ **特别注意**:必须识别屏幕上**所有**的消息,特别是位于**屏幕最底部**的消息,哪怕只有一部分,也要识别!
【重要判别规则】
1. 👤 **发送者 (Sender)**:
@@ -95,13 +96,19 @@ async def get_vlm_analysis(image_path):
- **极短 (1"-2")**:气泡非常短,形状接近一个小正方形。
- **极长 (60")**:气泡很长,宽度接近屏幕的一半。
- **内容**:气泡内**只有一个**表示时长的数字(如 `8"`)和一个声波图标。
- **辅助文字**:语音气泡右侧可能会有灰色的“转文字”或“取消转文字”字样,**请忽略这些文字**,依然将该气泡识别为语音消息!
- **绝对排除**:凡是包含汉字、长句子的气泡,**统统不是**语音消息。
3. 📝 **文本消息 (Text)**:
- **视觉特征**:气泡内包含汉字、标点符号、表情等文本内容。
4. 🔴 **未读状态 (Unread)**:
- **特征**:语音气泡右上角或附近有一个明显的**小红点**。
4. 🔴 **未读状态 (Unread) - 极度重要!**:
- **特征**:语音气泡的右上角(或紧邻右侧)有一个明显的**红色圆形小点**。
- **判别**:
- 只要看到红色小点,`is_unread` 必须为 **true**。
- 如果没有红色小点,`is_unread` 为 false。
- **注意**:红点可能很小,请仔细观察!这是判断是否处理的关键依据。即使红点旁边有灰色的“转文字”字样,只要有红点,就是未读!
【坐标系统】
**必须使用 [0-1000] 的归一化坐标系。**
@@ -133,7 +140,7 @@ async def get_vlm_analysis(image_path):
注意:
1. 坐标 `center` 和 `input_box` 必须是 [0-1000] 的归一化坐标。
2. `status` 判断:如果语音气泡的正下方紧挨着一条文本消息(通常是转换出的文字),则为 `converted`,否则为 `unconverted`。
3. `is_unread` 判断:如果有红点则为 true,否则为 false (仅针对语音消息)。
3. `is_unread` 判断:务必准确识别红点!如果有红点则为 true。
4. 请按从上到下的顺序输出所有消息。
"""
@@ -392,11 +399,96 @@ def find_template_match(screen_path, template_path, threshold=0.8):
logger.error(f"Template matching failed: {e}")
return None
def find_all_template_matches(screen_path, template_path, threshold=0.8):
"""
使用 OpenCV 模板匹配寻找**所有**符合条件的坐标
"""
try:
if not os.path.exists(template_path):
logger.error(f"Template file not found: {template_path}")
return []
img = cv2.imread(screen_path)
template = cv2.imread(template_path)
if img is None or template is None:
return []
h, w = template.shape[:2]
res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
# 找到所有大于阈值的点
loc = np.where(res >= threshold)
points = []
for pt in zip(*loc[::-1]): # Switch collumns and rows
center_x = pt[0] + w // 2
center_y = pt[1] + h // 2
points.append((center_x, center_y))
# 简单的去重(非极大值抑制的简化版,合并相近的点)
# 这里假设红点不会重叠,暂时直接返回,或者做一个简单的聚类
# 实际应用中,matchTemplate 对同一个目标周围可能会有多个连续的匹配点
# 我们需要合并它们
unique_points = []
for p in points:
is_close = False
for up in unique_points:
if abs(p[0] - up[0]) < 10 and abs(p[1] - up[1]) < 10:
is_close = True
break
if not is_close:
unique_points.append(p)
if unique_points:
logger.info(f"Found {len(unique_points)} matches for {os.path.basename(template_path)}")
return unique_points
except Exception as e:
logger.error(f"find_all_template_matches failed: {e}")
return []
def perform_input_action(d, center_point, text, auto_send=True):
"""
执行输入操作
"""
try:
# --- 新增逻辑:确保处于文字输入模式 ---
logger.info("正在检查输入模式...")
tmp_check_shot = os.path.join(os.path.dirname(os.path.abspath(__file__)), "temp_input_check.jpg")
d.screenshot(tmp_check_shot)
wen_zi_template = r"d:\dsWork\aiData\WeiXin\Templates\wen_zi_input.jpg"
input_text_template = r"d:\dsWork\aiData\WeiXin\Templates\input_text.jpg"
# 1. 检查是否存在 '切换到文字' 图标 (表示当前是语音模式)
# 注意:这里假设 wen_zi_input.jpg 是那个“键盘”图标
wen_zi_pos = find_template_match(tmp_check_shot, wen_zi_template, threshold=0.8)
if wen_zi_pos:
logger.info(f"检测到语音模式 (找到切换文字图标: {wen_zi_pos}),点击切换...")
d.click(wen_zi_pos[0], wen_zi_pos[1])
time.sleep(1.0) # 等待 UI 切换
else:
# 2. 如果没找到切换图标,假设是文字模式,尝试点击输入区域标识
logger.info("未检测到语音模式切换图标,尝试寻找文字输入区域...")
input_text_pos = find_template_match(tmp_check_shot, input_text_template, threshold=0.8)
if input_text_pos:
logger.info(f"找到文字输入区域标识 (input_text.jpg): {input_text_pos},点击激活...")
d.click(input_text_pos[0], input_text_pos[1])
time.sleep(0.5)
else:
logger.info("未找到特定的输入区域标识,将使用默认坐标或控件查找。")
# 清理临时文件
if os.path.exists(tmp_check_shot):
try:
os.remove(tmp_check_shot)
except:
pass
# --- 新增逻辑结束 ---
# 1. 尝试找到原生输入框并输入
edit_text = d(className="android.widget.EditText")
input_success = False