Files
aiData/Test/TestAdClick.py
HuangHai fdd5532b33 'commit'
2026-01-15 10:16:26 +08:00

181 lines
7.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import uiautomator2 as u2
import time
import os
import cv2
import numpy as np
def test_click():
d = u2.connect()
w, h = d.window_size()
print(f"Device size: {w}x{h}")
# 1. Take screenshot
print("Taking screenshot...")
screenshot_path = r"d:\dsWork\aiData\Output\debug_ad_before.jpg"
d.screenshot(screenshot_path)
# 2. Check hierarchy
print("Dumping hierarchy...")
try:
xml = d.dump_hierarchy()
with open(r"d:\dsWork\aiData\Output\hierarchy.xml", "w", encoding="utf-8") as f:
f.write(xml)
print(r"Hierarchy saved to d:\dsWork\aiData\Output\hierarchy.xml")
except Exception as e:
print(f"Failed to dump hierarchy: {e}")
# 3. Visualize the current fixed point (80/1000, 835/1000)
# Norm: 0.08, 0.835
# Previous attempt was: (86, 2004) for 1080x2400
# Let's try to detect the black circle using HSV or thresholding
img = cv2.imread(screenshot_path)
if img is None:
print("Failed to load screenshot")
return
# ROI: Left side, bottom half
# x: 0 - 200, y: 1500 - 2200 (approx for 1080x2400)
roi_x1, roi_x2 = 0, int(w * 0.25)
roi_y1, roi_y2 = int(h * 0.6), int(h * 0.9)
roi = img[roi_y1:roi_y2, roi_x1:roi_x2]
# Convert to grayscale
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
# The close button is a black circle with a white X.
# We look for a dark circle, and then check if it contains something bright.
candidates = []
# 尝试多个阈值以应对不同的亮度环境
for threshold_val in [40, 60, 80, 100]:
_, thresh = cv2.threshold(gray, threshold_val, 255, cv2.THRESH_BINARY_INV)
# Find contours
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
# 圆形度检查
perimeter = cv2.arcLength(cnt, True)
if perimeter == 0: continue
circularity = 4 * np.pi * (area / (perimeter * perimeter))
# 兔子广告关闭按钮通常很小 (40x40 左右在 1080p 下是 1600 面积)
if 100 < area < 4000 and circularity > 0.4:
# 获取该候选区域的 bounding box
x, y, w_cnt, h_cnt = cv2.boundingRect(cnt)
# 在这个黑色圆内部,检查是否有亮色的 'X'
# 我们可以对这个区域做反向阈值,找亮色物体
padding = 2
inner_roi = gray[max(0, y-padding):min(roi.shape[0], y+h_cnt+padding),
max(0, x-padding):min(roi.shape[1], x+w_cnt+padding)]
# 找亮色物体 (X)
_, inner_thresh = cv2.threshold(inner_roi, 180, 255, cv2.THRESH_BINARY)
inner_contours, _ = cv2.findContours(inner_thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
has_x = False
for i_cnt in inner_contours:
i_area = cv2.contourArea(i_cnt)
# X 应该比圆小很多
if 10 < i_area < area * 0.5:
has_x = True
break
if has_x:
M = cv2.moments(cnt)
if M["m00"] != 0:
cX = int(M["m10"] / M["m00"]) + roi_x1
cY = int(M["m01"] / M["m00"]) + roi_y1
norm_x = int(cX / w * 1000)
norm_y = int(cY / h * 1000)
# 避免重复
if not any(abs(cX - c[0]) < 15 and abs(cY - c[1]) < 15 for c in candidates):
candidates.append((cX, cY, area, norm_x, norm_y, True)) # True means found X
# 保存候选区域图片以便调试
cv2.imwrite(rf"d:\dsWork\aiData\Output\cand_{len(candidates)-1}_roi.jpg", inner_roi)
else:
# 如果没找到 X但圆形度很高也可以作为一个低优先级候选
if circularity > 0.7:
M = cv2.moments(cnt)
if M["m00"] != 0:
cX = int(M["m10"] / M["m00"]) + roi_x1
cY = int(M["m01"] / M["m00"]) + roi_y1
norm_x = int(cX / w * 1000)
norm_y = int(cY / h * 1000)
if not any(abs(cX - c[0]) < 15 and abs(cY - c[1]) < 15 for c in candidates):
candidates.append((cX, cY, area, norm_x, norm_y, False))
cv2.imwrite(rf"d:\dsWork\aiData\Output\cand_{len(candidates)-1}_roi.jpg", inner_roi)
# Save thresh image for debugging
cv2.imwrite(r"d:\dsWork\aiData\Output\debug_thresh.jpg", thresh)
# 评分逻辑
def score_candidate(c):
# c = (cx, cy, area, nx, ny, has_x)
has_x = c[5]
# 基础分:如果有 X大幅加分
score = 1000 if has_x else 0
# 距离分:越靠近预期的 (93, 830) 分越高
dist = np.sqrt((c[3] - 93)**2 + (c[4] - 830)**2)
score -= dist * 2
# 面积分:理想面积在 500-1500 之间
if 500 < c[2] < 1500:
score += 200
return score
candidates.sort(key=score_candidate, reverse=True)
target_x, target_y = int(w * 0.08), int(h * 0.835) # Default fallback
norm_target_x, norm_target_y = 80, 835
if candidates:
print(f"Found {len(candidates)} candidate close buttons via CV:")
for i, (cx, cy, area, nx, ny, has_x) in enumerate(candidates):
score = score_candidate((cx, cy, area, nx, ny, has_x))
print(f" Candidate {i}: ({cx}, {cy}), Area: {area}, Norm: ({nx}, {ny}), HasX: {has_x}, Score: {score:.2f}")
# Pick the best one
best_c = candidates[0]
target_x, target_y = best_c[0], best_c[1]
norm_target_x, norm_target_y = best_c[3], best_c[4]
print(f"Selected CV target: ({target_x}, {target_y}), Norm: ({norm_target_x}, {norm_target_y}), HasX: {best_c[5]}")
# Visualize
viz_img = cv2.imread(screenshot_path)
cv2.circle(viz_img, (target_x, target_y), 3, (0, 0, 255), -1)
debug_path = r"d:\dsWork\aiData\Output\debug_ad_point.jpg"
red_point_path = r"d:\dsWork\aiData\Output\_redpoint.jpg"
cv2.imwrite(debug_path, viz_img)
cv2.imwrite(red_point_path, viz_img)
print(f"Debug images saved to {debug_path} and {red_point_path}")
# 4. Perform click
print(f"Clicking ({target_x}, {target_y}) using single click...")
d.click(target_x, target_y)
print("Waiting 3s for user to observe if the ad is closed...")
time.sleep(3)
# print("Pressing BACK to return to main page...")
# d.press("back")
else:
print("No CV candidates found. Skipping click to avoid accidental background interaction.")
time.sleep(2)
# 5. Take after screenshot
after_path = r"d:\dsWork\aiData\Output\debug_ad_after.jpg"
d.screenshot(after_path)
print(f"After screenshot saved to {after_path}")
# Check if successful (compare image hash or just file size, though file size varies with compression)
# Simple check: If the black button is gone, the area should be brighter?
# Or just let user check.
if __name__ == "__main__":
test_click()