侧边栏壁纸
博主头像
いちごみるく

行动起来,活在当下

  • 累计撰写 70 篇文章
  • 累计创建 48 个标签
  • 累计收到 55 条评论

目 录CONTENT

文章目录

ddddocr 滑块图像匹配:历史案例与坐标换算

历史技术记录。 本文以 2022 年页面结构为背景,讲解离线图片匹配、坐标换算和轨迹生成。图像匹配结果不等于服务端验证通过;示例不保证适用于当前验证码。

一、ddddocr 在这里负责什么

这次实验使用 ddddocr 的 slide_match,给它滑块图片和背景图片,获得候选矩形坐标。图像匹配、文本 OCR、目标检测与按指定顺序点图标是不同任务,不能由一个接口的结果推出全部验证码都能完成。

2022 年测试页面的图片原始尺寸是 300×150,页面显示为 260×130。这个比例只属于当时页面;现在应读取实际图片宽度与渲染宽度,还要考虑裁剪、初始位置和 CSS 变换。

2022 年滑块页面观察

二、先在本地图片上确认匹配结果

安装环境后,准备自己的 target.png 和 background.png:

python -m pip install ddddocr
from pathlib import Path
import ddddocr


def match_local(target_path, background_path, simple_target=False):
    matcher = ddddocr.DdddOcr(det=False, ocr=False)
    result = matcher.slide_match(
        Path(target_path).read_bytes(),
        Path(background_path).read_bytes(),
        simple_target=simple_target)
    box = result.get('target')
    if not isinstance(box, (list, tuple)) or len(box) != 4:
        raise ValueError(f'未得到预期的匹配矩形:{result}')
    x1, y1, x2, y2 = box
    if x2 <= x1 or y2 <= y1:
        raise ValueError(f'匹配矩形无效:{box}')
    return result


if __name__ == '__main__':
    print(match_local('target.png', 'background.png'))

当前上游示例将 target 解读为 [x1, y1, x2, y2]。透明滑块和不带透明背景的目标可能要使用不同设置;simple_target=True 不是所有图片的默认答案。官方滑块匹配用法

如果原图来自 data URI,应先分离元信息、验证 Base64,再解码,不用一个贪婪正则假定所有内容都一定是图片:

import base64


def decode_data_image(value):
    header, separator, payload = value.partition(',')
    if not separator or not header.startswith('data:image/') or ';base64' not in header:
        raise ValueError('预期 Base64 图片 data URI')
    return base64.b64decode(payload, validate=True)

三、从图片坐标到显示坐标

无裁剪、无额外变换且匹配坐标确实以完整背景左上角为原点时,可以按宽度缩放:

def image_x_to_css(x_image, image_width, rendered_width):
    if image_width <= 0 or rendered_width <= 0:
        raise ValueError('图片宽度必须为正')
    return x_image * rendered_width / image_width

这个函数只返回背景里的显示位置。真正的拖动位移还要减去滑块当前显示位置,并考虑匹配器是否裁去了滑块透明边缘;缺口左边缘、拼图边缘与按钮中心可能不是同一坐标。缩放不均匀、页面滚动或设备像素比变化也要按实际渲染关系检查,不能始终写 target[0] * 260 / 300。

四、原轨迹和浏览器片段为何不能沿用

旧 get_track() 用固定初速、加速和减速积分,没有步数上限;长距离情况下速度可能减成负值,累计位移倒退,循环无法结束。逐步 round() 也使整数位移和原浮点累计量不同,所有动作相加不保证等于目标距离。

在自有测试页面或动画中,需要生成位移序列时,可以用有上限的插值并检查总和;它只是数学轨迹,不代表任何验证码服务认可的用户行为:

def integer_steps(distance, count=20):
    if distance < 0 or count <= 0:
        raise ValueError('distance 必须非负,count 必须为正')
    target = round(distance)
    points = [round(target * i / count) for i in range(count + 1)]
    steps = [right - left for left, right in zip(points, points[1:])]
    assert sum(steps) == target
    return steps

原 move_target(drive) 中同时使用 drive、未定义的 driver 和 self.driver,会产生 NameError;这不是识别率问题。程序还应区分元素不存在、图片解析失败、匹配失败和服务端拒绝,不通过继续尝试掩盖错误。

2022 年进一步验证页面

滑动后是否出现图标验证取决于服务端流程,不能把某次没有出现额外验证的现象当作固定规则;进一步验证出现时应按页面交互完成,不能由 ddddocr 这个库的名字推定它会识别所要求的图标顺序。离线图像匹配示例不验证当前携程页面的完整交互流程。

0

评论区