V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
aizya
V2EX  ›  程序员

如何提高滑块验证码的识别精度?

  •  
  •   aizya · 108 天前 · 1519 次点击
    这是一个创建于 108 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近碰到一个需求,需要自动化操作一个网站,第一步就遇到一个问题,滑块验证码登录。

    核心逻辑是:

    1. 获取两张图片,一张左侧的验证码图片,一张完整的图片
    2. 比对两张图片,获取到验证码图片与完整图片重合区域在 X 轴上需要移动的举例
    3. 通过 selenium 缓慢移动验证码图片到重合区域,完成登录
    

    在网上找到一个类似的需求和实现方案,跟着写了下代码 https://blog.csdn.net/weixin_53300120/article/details/138068169

    写了两个实现的方式:

    1. 采用 ddddocr:

       with open('left.png', 'rb') as f:
      
       target_bytes = f.read()
      
       with open('full.png', 'rb') as f:
           background_bytes = f.read()
      
       ocr = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
      
       res = ocr.slide_match(target_bytes, background_bytes, simple_target=True)
      
       print(res, res['target'][0])
      
    2. 采用 cv2:

       # 加载图片 A 和 B
       image_a = cv2.imread('left.png', cv2.IMREAD_GRAYSCALE)
       image_b = cv2.imread('full.png', cv2.IMREAD_GRAYSCALE)
      
       # 使用模板匹配方法( Template Matching )寻找图片 A 在 B 中的位置
       result = cv2.matchTemplate(image_b, image_a, cv2.TM_CCOEFF_NORMED)
      
       # 获取最大匹配值及其位置
       min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
      
       # 如果 TM_CCOEFF_NORMED 方法使用,可以通过以下方式找到匹配位置的左上角坐标
       top_left = max_loc
      
       # 获取图片 A 的宽度和高度
       height, width = image_a.shape
       print(height, ':', width)
       height2, width2 = image_b.shape
       print(height2, ':', width2)
      
       # 获取最左侧的 x 坐标(左上角)
       leftmost_x = max_loc[0]
       print('leftmost_x:', leftmost_x)
      

    现在的问题是,这两种方法的准确度都不高,而且我也不了解他们内部的实现方式,不知道如何提高识别的准确度。 现在用了一个比较笨的方法,死循环,重复 10 次以内大概率是可以成功一次。

    想请教大家,这种场景有没有啥好的办法可以提高识别的准确度,或者有没有相关的开源库可以直接用?

    感谢!

    15 条回复    2024-07-25 19:57:54 +08:00
    deepall
        1
    deepall  
       108 天前
    不知道你的 left.png 和 full.png 是啥样,我用 ddddocr 和 cv2 识别验证码的准确率很高
    aizya
        2
    aizya  
    OP
       108 天前
    @deepall 图片会动态变化,大概是这样的:

    full png:



    left png:

    deepall
        3
    deepall  
       108 天前
    这个 ddddocr 感觉不太行,试试这个
    def generate_distance(slice_url, bg_url):
    """
    :param bg_url: 背景图地址
    :param slice_url: 滑块图地址
    :return: distance
    :rtype: Integer
    """
    slice_image = np.asarray(bytearray(requests.get(slice_url).content), dtype=np.uint8)
    slice_image = cv2.imdecode(slice_image, 1)
    slice_image = cv2.Canny(slice_image, 255, 255)

    bg_image = np.asarray(bytearray(requests.get(bg_url).content), dtype=np.uint8)
    bg_image = cv2.imdecode(bg_image, 1)
    bg_image = cv2.pyrMeanShiftFiltering(bg_image, 5, 50)
    bg_image = cv2.Canny(bg_image, 255, 255)

    result = cv2.matchTemplate(bg_image, slice_image, cv2.TM_CCOEFF_NORMED)

    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)

    return max_loc[0]
    aizya
        4
    aizya  
    OP
       108 天前
    @deepall #3 一样的,也需要多次才能成功。
    naythefirst01
        5
    naythefirst01  
       108 天前
    这样的直接模板匹配就可以了 准确率很低是不是图片获取的有问题 比如没有下载原始图或者没有处理缩放之类的
    uTOmOuk3L6sb4MSI
        6
    uTOmOuk3L6sb4MSI  
       107 天前 via iPhone
    opencv 就行,成功率很高。我觉得文字识别和点选是更难的,不同大小、角度、颜色,甚至还有线条之类的干扰元素。我就会点滑块的,感觉不难。
    uTOmOuk3L6sb4MSI
        7
    uTOmOuk3L6sb4MSI  
       107 天前 via iPhone
    我不明白为什么要很多次成功,cv2 这种模板匹配只有成功和不成功吧,执行再多次应该也是一种结果,不会出现一会失败一会成功的结果。
    brianinzz
        8
    brianinzz  
       107 天前
    @ODD10 意思应该是给出的结果不对导致滑动了但是没通过
    uTOmOuk3L6sb4MSI
        9
    uTOmOuk3L6sb4MSI  
       107 天前 via iPhone
    @brianinzz 不对可能就是得加减 x ,y 透明部份?一般没这么严格。说得不清不楚。看线框很吻合了,比手动都吻合。
    aizya
        10
    aizya  
    OP
       107 天前
    @ODD10 #7 不不,执行多次是换张图片继续试,遇到简单的就过了。 不是一个图片跑多次。。
    uTOmOuk3L6sb4MSI
        11
    uTOmOuk3L6sb4MSI  
       107 天前 via iPhone
    @aizya full 和 left 不是配对的吗?怎么只有一张图?而且 y 轴可能都不一样,缺口可能也不一样
    aizya
        12
    aizya  
    OP
       107 天前
    @ODD10 #11 是两张图啊,你仔细看我 2 楼的回复
    uTOmOuk3L6sb4MSI
        13
    uTOmOuk3L6sb4MSI  
       107 天前 via iPhone
    @aizya 反正我可以,而且我测试过几家滑块,成功率蛮高的
    aizya
        14
    aizya  
    OP
       107 天前
    @ODD10 #13 方便提供一下你上面的代码吗?我运行看看问题出在什么地方?
    uTOmOuk3L6sb4MSI
        15
    uTOmOuk3L6sb4MSI  
       107 天前
    没什么特别的,我用的 js

    ```js

    const cv = require('@u4/opencv4nodejs');

    // 加载图像
    const originalMat = cv.imread(__dirname + '/tmp/' + 'full.png');
    const templateMat = cv.imread(__dirname + '/tmp/' + 'left.png');

    //识别图片边缘
    bg_edge = originalMat.canny(100, 200);
    cut_edge = templateMat.canny(100, 200);

    // 转换图片格式
    bg_pic = bg_edge.cvtColor(cv.COLOR_GRAY2RGB);
    cut_pic = cut_edge.cvtColor(cv.COLOR_GRAY2RGB);

    // 进行模板匹配
    const matched = bg_pic.matchTemplate(cut_pic, cv.TM_CCOEFF_NORMED);

    // 归一化
    const minMax = matched.minMaxLoc();
    const {
    maxLoc: { x, y },
    } = minMax;

    console.log(JSON.stringify(minMax));

    // 绘制匹配区域的矩形框
    const point = new cv.Point(x, y);
    const color = new cv.Vec(255, 0, 0);
    const thickness = 2;
    originalMat.drawRectangle(
    new cv.Rect(point.x, point.y, templateMat.cols, templateMat.rows),
    color,
    thickness,
    cv.LINE_8
    );

    // 显示结果
    cv.imshow('Result', originalMat);
    cv.waitKey();

    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1662 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 16:49 · PVG 00:49 · LAX 08:49 · JFK 11:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.