'commit'
This commit is contained in:
@@ -137,6 +137,110 @@ def is_background_dimmed(image_path, threshold=80):
|
||||
return avg_brightness < threshold
|
||||
|
||||
|
||||
def detect_rabbit_ad_close(image_path, debug_dir=None):
|
||||
"""
|
||||
通过图形学算法检测“新电兔AI”广告的关闭按钮
|
||||
特征:黑色圆,中间有白色X
|
||||
:param image_path: 截图路径
|
||||
:param debug_dir: 调试图保存目录
|
||||
:return: (x, y) 归一化坐标,如果未找到返回 None
|
||||
"""
|
||||
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]
|
||||
|
||||
# ROI: 左侧,中下部 (x: 0-25%, y: 60-90%)
|
||||
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]
|
||||
|
||||
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
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))
|
||||
|
||||
# 兔子广告关闭按钮通常很小 (面积在 100-4000 之间)
|
||||
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 or 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, has_x))
|
||||
|
||||
if not candidates:
|
||||
return None
|
||||
|
||||
# 评分逻辑
|
||||
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)
|
||||
best = candidates[0]
|
||||
|
||||
logger.info(f"CV detected rabbit ad close button at Norm({best[3]}, {best[4]}) with score {score_candidate(best):.2f}")
|
||||
|
||||
if debug_dir:
|
||||
os.makedirs(debug_dir, exist_ok=True)
|
||||
debug_img = img.copy()
|
||||
cv2.circle(debug_img, (best[0], best[1]), 10, (0, 0, 255), -1) # 使用红点表示识别结果
|
||||
save_image(os.path.join(debug_dir, "debug_rabbit_ad_cv.jpg"), debug_img)
|
||||
|
||||
return [best[3], best[4]]
|
||||
|
||||
|
||||
def detect_black_agree_button(image_path, debug_dir=None):
|
||||
"""
|
||||
通过计算机图形学检测黑色的"同意"按钮 (Image 1 场景)
|
||||
|
||||
Reference in New Issue
Block a user