| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- import cv2
- import numpy as np
- from PIL import Image
- import os
- import sys
- def detect_slider_button(image_path, output_path=None):
- """
- 识别滑块验证码底部拖动滑块按钮的位置,返回其四边坐标,并用红框标记。
- 改进版:只识别滑块轨道内的橙色小方块按钮,避免把轨道边框识别进去。
- 参数:
- image_path: 输入图片路径
- output_path: 输出图片路径,默认在原图目录下生成 <原图名>_slider_red.png
- 返回:
- coords: dict, 包含 top_left, top_right, bottom_left, bottom_right
- output_path: 红框标记图路径
- """
- img = Image.open(image_path)
- img_np = np.array(img)
- h, w = img_np.shape[:2]
- # 只检测图片下半部分
- start_y = int(h * 0.55)
- lower_half = img_np[start_y:, :]
- # 转换为HSV颜色空间
- hsv = cv2.cvtColor(lower_half, cv2.COLOR_RGB2HSV)
- # 检测橙色滑块按钮(严格范围,只保留高饱和高亮度的按钮内部)
- lower = np.array([8, 230, 230])
- upper = np.array([22, 255, 255])
- mask = cv2.inRange(hsv, lower, upper)
- # 形态学操作去噪
- kernel = np.ones((5, 5), np.uint8)
- mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
- mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
- # 查找轮廓
- contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
- if not contours:
- raise ValueError("未检测到滑块按钮")
- # 筛选轮廓:近似正方形、面积适中、位于轨道区域内
- candidates = []
- for cnt in contours:
- x, y, bw, bh = cv2.boundingRect(cnt)
- area = bw * bh
- if area < 300 or bw < 30 or bh < 30:
- continue
- aspect_ratio = min(bw, bh) / max(bw, bh) if max(bw, bh) > 0 else 0
- if aspect_ratio < 0.5:
- continue
- candidates.append((cnt, area, aspect_ratio, x, y, bw, bh))
- if not candidates:
- raise ValueError("未找到合适的滑块按钮")
- # 选择最像滑块按钮的:面积适中、宽高比接近1
- best = max(candidates, key=lambda c: c[1] * c[2])
- _, _, _, x, y, bw, bh = best
- # 转换回全图坐标
- x = x
- y = y + start_y
- # 四边坐标
- top_left = (x, y)
- top_right = (x + bw, y)
- bottom_left = (x, y + bh)
- bottom_right = (x + bw, y + bh)
- coords = {
- "top_left": top_left,
- "top_right": top_right,
- "bottom_left": bottom_left,
- "bottom_right": bottom_right,
- }
- # 在原图上画红框
- canvas = img_np.copy()
- cv2.rectangle(canvas, top_left, bottom_right, (255, 0, 0), 2)
- # 保存红框标记图
- if output_path is None:
- base, ext = os.path.splitext(image_path)
- output_path = f"{base}_slider_red.png"
- cv2.imwrite(output_path, cv2.cvtColor(canvas, cv2.COLOR_RGB2BGR))
- return coords, output_path
- if __name__ == "__main__":
- if len(sys.argv) < 2:
- print("用法: python detect_slider_button.py <图片路径> [输出路径]")
- sys.exit(1)
- image_path = sys.argv[1]
- output_path = sys.argv[2] if len(sys.argv) > 2 else None
- coords, out = detect_slider_button(image_path, output_path)
- print(f"滑块按钮四边坐标:")
- print(f" 左上: {coords['top_left']}")
- print(f" 右上: {coords['top_right']}")
- print(f" 左下: {coords['bottom_left']}")
- print(f" 右下: {coords['bottom_right']}")
- print(f"红框标记图: {out}")
|