'commit'
This commit is contained in:
@@ -44,10 +44,11 @@ def test_click():
|
||||
# Convert to grayscale
|
||||
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
# The close button is a black circle. So it should be very dark.
|
||||
# Try multiple thresholds to find the best candidates
|
||||
# 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 [50, 70, 90, 110]:
|
||||
# 尝试多个阈值以应对不同的亮度环境
|
||||
for threshold_val in [40, 60, 80, 100]:
|
||||
_, thresh = cv2.threshold(gray, threshold_val, 255, cv2.THRESH_BINARY_INV)
|
||||
|
||||
# Find contours
|
||||
@@ -55,82 +56,114 @@ def test_click():
|
||||
|
||||
for cnt in contours:
|
||||
area = cv2.contourArea(cnt)
|
||||
# Approximate circle check
|
||||
# 圆形度检查
|
||||
perimeter = cv2.arcLength(cnt, True)
|
||||
if perimeter == 0: continue
|
||||
circularity = 4 * np.pi * (area / (perimeter * perimeter))
|
||||
|
||||
# Filter by size and circularity
|
||||
# Size: it's a small button
|
||||
if 50 < area < 6000 and circularity > 0.35:
|
||||
M = cv2.moments(cnt)
|
||||
if M["m00"] != 0:
|
||||
cX = int(M["m10"] / M["m00"]) + roi_x1
|
||||
cY = int(M["m01"] / M["m00"]) + roi_y1
|
||||
|
||||
# Calculate normalized coordinates
|
||||
norm_x = int(cX / w * 1000)
|
||||
norm_y = int(cY / h * 1000)
|
||||
|
||||
# Avoid duplicates
|
||||
if not any(abs(cX - c[0]) < 10 and abs(cY - c[1]) < 10 for c in candidates):
|
||||
candidates.append((cX, cY, area, norm_x, norm_y))
|
||||
# 兔子广告关闭按钮通常很小 (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
|
||||
_, debug_thresh = cv2.threshold(gray, 70, 255, cv2.THRESH_BINARY_INV)
|
||||
cv2.imwrite(r"d:\dsWork\aiData\Output\debug_thresh.jpg", debug_thresh)
|
||||
cv2.imwrite(r"d:\dsWork\aiData\Output\debug_thresh.jpg", thresh)
|
||||
|
||||
# Sort by proximity to expected location (Left side, Middle-Bottom)
|
||||
# Expected norm: (93, 830)
|
||||
# 评分逻辑
|
||||
def score_candidate(c):
|
||||
# Weighting: X proximity is important, Y proximity is also important, Area around 400-500 is ideal
|
||||
dist_x = abs(c[3] - 93)
|
||||
dist_y = abs(c[4] - 830)
|
||||
area_diff = abs(c[2] - 450) / 450.0
|
||||
return dist_x * 2 + dist_y + area_diff * 100
|
||||
# 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)
|
||||
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) in enumerate(candidates):
|
||||
score = score_candidate((cx, cy, area, nx, ny))
|
||||
print(f" Candidate {i}: ({cx}, {cy}), Area: {area}, Norm: ({nx}, {ny}), Score: {score:.2f}")
|
||||
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})")
|
||||
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. Using fixed coordinates.")
|
||||
|
||||
# Visualize
|
||||
cv2.circle(img, (target_x, target_y), 20, (0, 255, 0), -1)
|
||||
cv2.putText(img, f"Click ({target_x}, {target_y})", (target_x + 30, target_y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
|
||||
|
||||
# Also draw the fixed point for comparison (Blue)
|
||||
fixed_x = int(w * 0.08)
|
||||
fixed_y = int(h * 0.835)
|
||||
cv2.circle(img, (fixed_x, fixed_y), 15, (255, 0, 0), -1)
|
||||
|
||||
debug_path = r"d:\dsWork\aiData\Output\debug_ad_point.jpg"
|
||||
cv2.imwrite(debug_path, img)
|
||||
print(f"Debug image saved to {debug_path}")
|
||||
|
||||
# 4. Perform click
|
||||
print(f"Clicking ({target_x}, {target_y}) using double_click...")
|
||||
# Use d.double_click for a faster interaction
|
||||
d.double_click(target_x, target_y, duration=0.05)
|
||||
|
||||
# NEW: Handle potential background click as per user's latest request
|
||||
print("Waiting 2s for potential background card to load...")
|
||||
time.sleep(2)
|
||||
print("Pressing BACK to return to main page...")
|
||||
d.press("back")
|
||||
print("No CV candidates found. Skipping click to avoid accidental background interaction.")
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user