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()