'commit'
This commit is contained in:
@@ -220,267 +220,8 @@ def detect_black_agree_button(image_path, debug_dir=None):
|
||||
return None
|
||||
|
||||
|
||||
def detect_ad_close_x(image_path, template_path, debug_dir=None, threshold=0.7):
|
||||
"""
|
||||
通过模板匹配检测"关闭(X)"按钮 (Image 2 场景)
|
||||
支持多尺度匹配
|
||||
:param image_path: 截图路径
|
||||
:param template_path: 模板图片路径
|
||||
:param debug_dir: 调试目录
|
||||
:param threshold: 匹配阈值
|
||||
:return: (x, y) 坐标中心点,如果未找到返回 None
|
||||
"""
|
||||
if not os.path.exists(image_path):
|
||||
return None
|
||||
|
||||
if not os.path.exists(template_path):
|
||||
logger.warning(f"Template not found: {template_path}")
|
||||
return None
|
||||
|
||||
target = read_image(image_path)
|
||||
template = read_image(template_path)
|
||||
|
||||
if target is None or template is None:
|
||||
return None
|
||||
|
||||
# 转换为灰度图进行匹配,减少颜色干扰
|
||||
target_gray = cv2.cvtColor(target, cv2.COLOR_BGR2GRAY)
|
||||
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
t_h, t_w = template_gray.shape[:2]
|
||||
|
||||
best_match = None
|
||||
|
||||
# 多尺度匹配: 缩放模板
|
||||
# 假设模板可能比实际大,也可能小。范围 0.5 - 1.5
|
||||
scales = np.linspace(0.5, 1.5, 20)
|
||||
|
||||
for scale in scales:
|
||||
# 计算缩放后的模板尺寸
|
||||
new_w = int(t_w * scale)
|
||||
new_h = int(t_h * scale)
|
||||
|
||||
# 确保缩放后的模板不大于目标图像
|
||||
if new_w > target_gray.shape[1] or new_h > target_gray.shape[0]:
|
||||
continue
|
||||
|
||||
resized_template = cv2.resize(template_gray, (new_w, new_h))
|
||||
|
||||
# 匹配
|
||||
result = cv2.matchTemplate(target_gray, resized_template, cv2.TM_CCOEFF_NORMED)
|
||||
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
||||
|
||||
if best_match is None or max_val > best_match[0]:
|
||||
best_match = (max_val, max_loc, scale, new_w, new_h)
|
||||
|
||||
if best_match is None:
|
||||
return None
|
||||
|
||||
max_val, max_loc, best_scale, best_w, best_h = best_match
|
||||
|
||||
# 获取目标图像尺寸
|
||||
target_h, target_w = target_gray.shape[:2]
|
||||
|
||||
top_left = max_loc
|
||||
center_x = top_left[0] + best_w // 2
|
||||
center_y = top_left[1] + best_h // 2
|
||||
|
||||
# --- 位置启发式过滤 ---
|
||||
# 计算相对位置
|
||||
rel_x = center_x / target_w
|
||||
rel_y = center_y / target_h
|
||||
|
||||
logger.info(
|
||||
f"Ad Close Button Match: Confidence={max_val:.4f}, Scale={best_scale:.2f}, Pos=({center_x}, {center_y}), Rel=({rel_x:.2f}, {rel_y:.2f})")
|
||||
|
||||
is_valid_pos = True
|
||||
|
||||
# 针对底部中心的特殊逻辑:如果是底部中心位置,降低置信度阈值要求
|
||||
is_in_bottom_center = (0.4 < rel_x < 0.6) and (rel_y > 0.65)
|
||||
effective_threshold = threshold
|
||||
if is_in_bottom_center:
|
||||
effective_threshold = min(threshold, 0.6) # 底部中心位置允许 0.6 的置信度
|
||||
logger.info(f"Bottom Center detected, lowering threshold to {effective_threshold}")
|
||||
|
||||
# 规则1: 过滤掉屏幕正中央偏上的区域 (通常是广告标题、图标或内容)
|
||||
# 范围: X在 [0.3, 0.7] 且 Y在 [0.15, 0.5]
|
||||
if 0.3 < rel_x < 0.7 and 0.15 < rel_y < 0.5:
|
||||
logger.warning(f"Ignored match at ({center_x}, {center_y}) - likely Ad Content/Title (Center-Top area).")
|
||||
is_valid_pos = False
|
||||
|
||||
# 规则2: 如果置信度不是特别高 (>0.98), 强制要求在典型区域 (右上角 或 底部中间)
|
||||
# 右上角: X > 0.7, Y < 0.5
|
||||
# 底部中间: Y > 0.6
|
||||
if is_valid_pos and max_val < 0.98:
|
||||
if not ((rel_x > 0.7 and rel_y < 0.5) or (rel_y > 0.6)):
|
||||
logger.warning(
|
||||
f"Ignored match at ({center_x}, {center_y}) - not in typical Close Button regions (Top-Right or Bottom).")
|
||||
is_valid_pos = False
|
||||
|
||||
if max_val >= effective_threshold:
|
||||
if is_valid_pos:
|
||||
# [Safety Check] 底部安全区排除
|
||||
# 如果检测到的关闭按钮位于屏幕底部 BOTTOM_SAFE_EXCLUDE_RATIO 区域内,认为是误判(如误触底部功能按钮)
|
||||
# 特例:如果按钮在水平中心附近 (0.4 < rel_x < 0.6),且垂直方向在 0.96 以内,则允许点击 (针对 2025 年度账单等插屏广告)
|
||||
is_in_bottom_center_safe = (0.4 < rel_x < 0.6) and (rel_y < 0.96)
|
||||
|
||||
if center_y > (target_h * (1 - BOTTOM_SAFE_EXCLUDE_RATIO)) and not is_in_bottom_center_safe:
|
||||
logger.warning(
|
||||
f"Ignored Ad Close Button at ({center_x}, {center_y}) - in Bottom Safety Zone ({int(BOTTOM_SAFE_EXCLUDE_RATIO * 100)}%).")
|
||||
# 也可以保存一下调试图
|
||||
if debug_dir:
|
||||
os.makedirs(debug_dir, exist_ok=True)
|
||||
debug_img = target.copy()
|
||||
cv2.rectangle(debug_img, top_left, (top_left[0] + best_w, top_left[1] + best_h), (0, 0, 128),
|
||||
2) # Dark Red for Safety Ignored
|
||||
cv2.putText(debug_img, f"SAFETY IGNORED",
|
||||
(top_left[0], top_left[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 128), 1)
|
||||
save_image(os.path.join(debug_dir, "debug_ad_close_safety_ignored.jpg"), debug_img)
|
||||
return None
|
||||
|
||||
logger.info(f"Found Ad Close Button at ({center_x}, {center_y})")
|
||||
|
||||
if debug_dir:
|
||||
os.makedirs(debug_dir, exist_ok=True)
|
||||
debug_img = target.copy()
|
||||
cv2.rectangle(debug_img, top_left, (top_left[0] + best_w, top_left[1] + best_h), (0, 0, 255), 2)
|
||||
cv2.circle(debug_img, (center_x, center_y), 5, (0, 255, 0), -1)
|
||||
cv2.putText(debug_img, f"Conf: {max_val:.2f}, Sc: {best_scale:.2f}",
|
||||
(top_left[0], top_left[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
|
||||
save_image(os.path.join(debug_dir, "debug_ad_close_x.jpg"), debug_img)
|
||||
|
||||
return (center_x, center_y)
|
||||
else:
|
||||
# 虽然置信度高,但是位置不对,保存为 False Positive 供调试
|
||||
if debug_dir:
|
||||
os.makedirs(debug_dir, exist_ok=True)
|
||||
debug_img = target.copy()
|
||||
cv2.rectangle(debug_img, top_left, (top_left[0] + best_w, top_left[1] + best_h), (0, 165, 255),
|
||||
2) # Orange for ignored
|
||||
cv2.putText(debug_img, f"IGNORED Pos",
|
||||
(top_left[0], top_left[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 165, 255), 1)
|
||||
save_image(os.path.join(debug_dir, "debug_ad_close_ignored.jpg"), debug_img)
|
||||
|
||||
# 如果没找到,但有一定置信度,也保存一下调试图以便分析
|
||||
if max_val > 0.4 and debug_dir:
|
||||
os.makedirs(debug_dir, exist_ok=True)
|
||||
debug_img = target.copy()
|
||||
top_left = max_loc
|
||||
cv2.rectangle(debug_img, top_left, (top_left[0] + best_w, top_left[1] + best_h), (0, 255, 255), 2)
|
||||
cv2.putText(debug_img, f"Failed Conf: {max_val:.2f}",
|
||||
(top_left[0], top_left[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1)
|
||||
save_image(os.path.join(debug_dir, "debug_ad_close_fail.jpg"), debug_img)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def detect_any_ad_close(image_path, template_dir, debug_dir=None):
|
||||
"""
|
||||
遍历模板目录下的所有 ad_close*.jpg 进行匹配
|
||||
"""
|
||||
if not os.path.exists(template_dir):
|
||||
return None
|
||||
|
||||
for filename in os.listdir(template_dir):
|
||||
if filename.startswith("ad_close") and filename.endswith(".jpg"):
|
||||
template_path = os.path.join(template_dir, filename)
|
||||
logger.info(f"Trying template: {filename}")
|
||||
pos = detect_ad_close_x(image_path, template_path, debug_dir=debug_dir)
|
||||
if pos:
|
||||
return pos
|
||||
return None
|
||||
|
||||
|
||||
def detect_bottom_close_circle(image_path, debug_dir=None):
|
||||
"""
|
||||
通过几何特征检测底部的圆形关闭按钮 (常见于插屏广告)
|
||||
特征:
|
||||
1. 位于屏幕底部区域 (Y > 60%)
|
||||
2. 水平居中 (X 靠近 W/2)
|
||||
3. 圆形或近似圆形
|
||||
4. 内部有高对比度边缘 (X号)
|
||||
"""
|
||||
if not os.path.exists(image_path):
|
||||
return None
|
||||
|
||||
img = read_image(image_path)
|
||||
if img is None:
|
||||
return None
|
||||
|
||||
h, w = img.shape[:2]
|
||||
|
||||
# 1. 提取感兴趣区域 (ROI): 屏幕中下部 (避开顶部的搜索栏和底部的导航栏)
|
||||
roi_top = int(h * 0.60)
|
||||
roi_bottom = int(h * 0.90)
|
||||
roi_h = roi_bottom - roi_top
|
||||
roi = img[roi_top:roi_bottom, :]
|
||||
|
||||
# 转灰度
|
||||
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
|
||||
# 高斯模糊降噪
|
||||
gray_blurred = cv2.GaussianBlur(gray, (9, 9), 2)
|
||||
|
||||
# 2. 霍夫圆变换检测圆形
|
||||
# dp=1.2 (累加器分辨率), minDist=w/5 (圆心最小距离), param1=100 (Canny高阈值), param2=30 (圆心累加阈值), minR=w*0.04, maxR=w*0.1
|
||||
# 缩小最大半径范围,防止识别到过大的按钮
|
||||
circles = cv2.HoughCircles(gray_blurred, cv2.HOUGH_GRADIENT, dp=1.2, minDist=w / 5,
|
||||
param1=100, param2=30, minRadius=int(w * 0.04), maxRadius=int(w * 0.09))
|
||||
|
||||
if circles is not None:
|
||||
circles = np.round(circles[0, :]).astype("int")
|
||||
|
||||
best_circle = None
|
||||
min_dist_to_center = float('inf')
|
||||
|
||||
for (cx, cy, r) in circles:
|
||||
# 还原到全图坐标
|
||||
global_cy = roi_top + cy
|
||||
global_cx = cx
|
||||
|
||||
# 过滤1: 必须在水平中心附近 (容差 15%)
|
||||
if abs(global_cx - w // 2) > (w * 0.15):
|
||||
continue
|
||||
|
||||
# 过滤2: 垂直方向限制 (必须在屏幕 65% - 88% 之间)
|
||||
# 这样可以避开位于底部的导航栏 (0.9+) 和顶部的卡片
|
||||
rel_y = global_cy / h
|
||||
if rel_y < 0.65 or rel_y > 0.88:
|
||||
continue
|
||||
|
||||
dist = abs(global_cx - w // 2)
|
||||
if dist < min_dist_to_center:
|
||||
min_dist_to_center = dist
|
||||
best_circle = (global_cx, global_cy, r)
|
||||
|
||||
if best_circle:
|
||||
cx, cy, r = best_circle
|
||||
# 确保转换为标准的 Python int,否则 uiautomator2 click 可能会报错 (JSON serializable error)
|
||||
cx, cy, r = int(cx), int(cy), int(r)
|
||||
|
||||
# [Safety Check] 底部安全区排除
|
||||
# 如果检测到的圆形按钮位于屏幕底部 BOTTOM_SAFE_EXCLUDE_RATIO 区域内,认为是误判(如误触底部扫码充电等)
|
||||
# 特例:如果按钮在水平中心附近 (0.4 < global_cx/w < 0.6),且垂直方向在 0.96 以内,则允许点击 (针对插屏广告)
|
||||
rel_x = cx / w
|
||||
rel_y = cy / h
|
||||
is_in_bottom_center = (0.4 < rel_x < 0.6) and (rel_y < 0.96)
|
||||
|
||||
if cy > (h * (1 - BOTTOM_SAFE_EXCLUDE_RATIO)) and not is_in_bottom_center:
|
||||
logger.warning(
|
||||
f"Ignored Bottom Circle at ({cx}, {cy}) - in Bottom Safety Zone ({int(BOTTOM_SAFE_EXCLUDE_RATIO * 100)}%).")
|
||||
return None
|
||||
|
||||
logger.info(f"Found Bottom Circle Button via Hough: ({cx}, {cy}), r={r}")
|
||||
|
||||
if debug_dir:
|
||||
os.makedirs(debug_dir, exist_ok=True)
|
||||
debug_img = img.copy()
|
||||
cv2.circle(debug_img, (cx, cy), r, (0, 255, 0), 2)
|
||||
cv2.circle(debug_img, (cx, cy), 2, (0, 0, 255), 3)
|
||||
save_image(os.path.join(debug_dir, "debug_bottom_circle.jpg"), debug_img)
|
||||
|
||||
return (cx, cy)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def find_expand_button_position(image_path, debug_dir=None, debug_filename_prefix=None):
|
||||
|
||||
@@ -28,6 +28,17 @@ async def check_and_close_ad(d):
|
||||
"""
|
||||
logger.info("开始检测广告弹窗 (VL方案)...")
|
||||
|
||||
# [新增] 策略:先向上滑动一点,触发悬浮广告(如兔子)收起/躲避
|
||||
try:
|
||||
w, h = d.window_size()
|
||||
logger.info(f"执行微小滑动 (Swipe Up),尝试触发悬浮广告收起...")
|
||||
# 从 70% 处滑到 50% 处,模拟手指上滑,页面内容上移
|
||||
d.swipe(w * 0.5, h * 0.7, w * 0.5, h * 0.5, duration=0.3)
|
||||
# 等待滑动动画结束和广告收起动画
|
||||
await asyncio.sleep(0.5)
|
||||
except Exception as e:
|
||||
logger.warning(f"滑动操作异常: {e}")
|
||||
|
||||
# 1. 拍摄截图
|
||||
t1 = time.time()
|
||||
image_uuid = str(uuid.uuid4())
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,46 +0,0 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 将项目根目录添加到 sys.path
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
if project_root not in sys.path:
|
||||
sys.path.append(project_root)
|
||||
|
||||
from Apps.XinDianTu import Kit
|
||||
|
||||
def verify():
|
||||
target_path = r"d:\dsWork\aiData\Output\Screenshot_20260112_215026.jpg"
|
||||
template_path = r"d:\dsWork\aiData\Apps\XinDianTu\Templates\TuZi.jpg"
|
||||
|
||||
if not os.path.exists(target_path):
|
||||
print(f"Target not found: {target_path}")
|
||||
return
|
||||
|
||||
if not os.path.exists(template_path):
|
||||
print(f"Template not found: {template_path}")
|
||||
return
|
||||
|
||||
match = Kit.find_template_match(target_path, template_path, threshold=0.7)
|
||||
|
||||
if match:
|
||||
cx, cy, w, h, val = match
|
||||
print(f"SUCCESS: Found rabbit at ({cx}, {cy}) with size {w}x{h}, confidence {val:.2f}")
|
||||
|
||||
# Calculate close button position (below the rabbit)
|
||||
# Based on visual estimation from user description, the close button is "below it".
|
||||
# Let's assume a safe offset. Usually ad close buttons are attached to the ad image or slightly below.
|
||||
# If the rabbit image includes the whole ad, the close button might be part of it or below.
|
||||
# If TuZi.jpg is just the rabbit head, the close button is below.
|
||||
|
||||
close_offset_y = h // 2 + 30 # Start from center, go down half height + 30px
|
||||
close_x = cx
|
||||
close_y = cy + h // 2 + 20 # Try to aim below the image bottom edge
|
||||
|
||||
print(f"Proposed close button click at: ({close_x}, {close_y})")
|
||||
|
||||
else:
|
||||
print("FAILED: Rabbit not found in the screenshot.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
verify()
|
||||
Reference in New Issue
Block a user