detect_slider_button.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import cv2
  2. import numpy as np
  3. from PIL import Image
  4. import os
  5. import sys
  6. def detect_slider_button(image_path, output_path=None):
  7. """
  8. 识别滑块验证码底部拖动滑块按钮的位置,返回其四边坐标,并用红框标记。
  9. 改进版:只识别滑块轨道内的橙色小方块按钮,避免把轨道边框识别进去。
  10. 参数:
  11. image_path: 输入图片路径
  12. output_path: 输出图片路径,默认在原图目录下生成 <原图名>_slider_red.png
  13. 返回:
  14. coords: dict, 包含 top_left, top_right, bottom_left, bottom_right
  15. output_path: 红框标记图路径
  16. """
  17. img = Image.open(image_path)
  18. img_np = np.array(img)
  19. h, w = img_np.shape[:2]
  20. # 只检测图片下半部分
  21. start_y = int(h * 0.55)
  22. lower_half = img_np[start_y:, :]
  23. # 转换为HSV颜色空间
  24. hsv = cv2.cvtColor(lower_half, cv2.COLOR_RGB2HSV)
  25. # 检测橙色滑块按钮(严格范围,只保留高饱和高亮度的按钮内部)
  26. lower = np.array([8, 230, 230])
  27. upper = np.array([22, 255, 255])
  28. mask = cv2.inRange(hsv, lower, upper)
  29. # 形态学操作去噪
  30. kernel = np.ones((5, 5), np.uint8)
  31. mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
  32. mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
  33. # 查找轮廓
  34. contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  35. if not contours:
  36. raise ValueError("未检测到滑块按钮")
  37. # 筛选轮廓:近似正方形、面积适中、位于轨道区域内
  38. candidates = []
  39. for cnt in contours:
  40. x, y, bw, bh = cv2.boundingRect(cnt)
  41. area = bw * bh
  42. if area < 300 or bw < 30 or bh < 30:
  43. continue
  44. aspect_ratio = min(bw, bh) / max(bw, bh) if max(bw, bh) > 0 else 0
  45. if aspect_ratio < 0.5:
  46. continue
  47. candidates.append((cnt, area, aspect_ratio, x, y, bw, bh))
  48. if not candidates:
  49. raise ValueError("未找到合适的滑块按钮")
  50. # 选择最像滑块按钮的:面积适中、宽高比接近1
  51. best = max(candidates, key=lambda c: c[1] * c[2])
  52. _, _, _, x, y, bw, bh = best
  53. # 转换回全图坐标
  54. x = x
  55. y = y + start_y
  56. # 四边坐标
  57. top_left = (x, y)
  58. top_right = (x + bw, y)
  59. bottom_left = (x, y + bh)
  60. bottom_right = (x + bw, y + bh)
  61. coords = {
  62. "top_left": top_left,
  63. "top_right": top_right,
  64. "bottom_left": bottom_left,
  65. "bottom_right": bottom_right,
  66. }
  67. # 在原图上画红框
  68. canvas = img_np.copy()
  69. cv2.rectangle(canvas, top_left, bottom_right, (255, 0, 0), 2)
  70. # 保存红框标记图
  71. if output_path is None:
  72. base, ext = os.path.splitext(image_path)
  73. output_path = f"{base}_slider_red.png"
  74. cv2.imwrite(output_path, cv2.cvtColor(canvas, cv2.COLOR_RGB2BGR))
  75. return coords, output_path
  76. if __name__ == "__main__":
  77. if len(sys.argv) < 2:
  78. print("用法: python detect_slider_button.py <图片路径> [输出路径]")
  79. sys.exit(1)
  80. image_path = sys.argv[1]
  81. output_path = sys.argv[2] if len(sys.argv) > 2 else None
  82. coords, out = detect_slider_button(image_path, output_path)
  83. print(f"滑块按钮四边坐标:")
  84. print(f" 左上: {coords['top_left']}")
  85. print(f" 右上: {coords['top_right']}")
  86. print(f" 左下: {coords['bottom_left']}")
  87. print(f" 右下: {coords['bottom_right']}")
  88. print(f"红框标记图: {out}")