基于图像处理的滑块验证码匹配技术

滑块验证码是一种常见的验证码形式,通过拖动滑块与背景图像中的缺口进行匹配,验证用户是否为真人。本文将详细介绍基于图像处理的滑块验证码匹配技术,并提供优化代码以提高滑块位置偏移量的准确度,尤其是在背景图滑块阴影较浅的情况下。

一、背景知识

1.1 图像处理概述

图像处理是指对图像进行分析和操作,以达到增强图像、提取特征、识别模式等目的。常用的图像处理技术包括高斯模糊、Canny 边缘检测、轮廓提取等。

1.2 滑块验证码的原理

滑块验证码通过用户拖动滑块,使滑块图像与背景图像中的缺口对齐,从而验证用户的操作。实现滑块验证码匹配的关键在于精确检测背景图像中缺口的位置。

二、技术实现

2.1 代码实现

import base64
import os
from datetime import datetime
from typing import Union, Optional

import cv2
import numpy as np


class SliderCaptchaMatch:
    def __init__(self,
                 gaussian_blur_kernel_size=(5, 5),
                 gaussian_blur_sigma_x=0,
                 canny_threshold1=200,
                 canny_threshold2=450,
                 save_images=False,
                 output_path=""):
        """
        初始化SlideMatch类

        :param gaussian_blur_kernel_size: 高斯模糊核大小,默认(5, 5)
        :param gaussian_blur_sigma_x: 高斯模糊SigmaX,默认0
        :param canny_threshold1: Canny边缘检测阈值1,默认200
        :param canny_threshold2: Canny边缘检测阈值2,默认450
        :param save_images: 是否保存过程图片,默认False
        :param output_path: 生成图片保存路径,默认当前目录
        """
        self.GAUSSIAN_BLUR_KERNEL_SIZE = gaussian_blur_kernel_size
        self.GAUSSIAN_BLUR_SIGMA_X = gaussian_blur_sigma_x
        self.CANNY_THRESHOLD1 = canny_threshold1
        self.CANNY_THRESHOLD2 = canny_threshold2
        self.save_images = save_images
        self.output_path = output_path

    def _remove_alpha_channel(self, image):
        """
        移除图像的alpha通道

        :param image: 输入图像
        :return: 移除alpha通道后的图像
        """
        if image.shape[2] == 4:  # 如果图像有alpha通道
            alpha_channel = image[:, :, 3]
            rgb_channels = image[:, :, :3]

            # 创建一个白色背景
            white_background = np.ones_like(rgb_channels, dtype=np.uint8) * 255

            # 使用alpha混合图像与白色背景
            alpha_factor = alpha_channel[:, :, np.newaxis] / 255.0
            image_no_alpha = rgb_channels * alpha_factor + white_background * (1 - alpha_factor)
            return image_no_alpha.astype(np.uint8)
        else:
            return image

    def _get_gaussian_blur_image(self, image):
        """
        对图像进行高斯模糊处理

        :param image: 输入图像
        :return: 高斯模糊处理后的图像
        """
        return cv2.GaussianBlur(image, self.GAUSSIAN_BLUR_KERNEL_SIZE, self.GAUSSIAN_BLUR_SIGMA_X)

    def _get_canny_image(self, image):
        """
        对图像进行Canny边缘检测

        :param image: 输入图像
        :return: Canny边缘检测后的图像
        """
        return cv2.Canny(image, self.CANNY_THRESHOLD1, self.CANNY_THRESHOLD2)

    def _get_contours(self, image):
        """
        获取图像的轮廓

        :param image: 输入图像
        :return: 轮廓列表
        """
        contours, _ = cv2.findContours(image, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
        return contours

    def _get_contour_area_threshold(self, image_width, image_height):
        """
        计算轮廓面积阈值

        :param image_width: 图像宽度
        :param image_height: 图像高度
        :return: 最小和最大轮廓面积阈值
        """
        contour_area_min = (image_width * 0.15) * (image_height * 0.25) * 0.8
        contour_area_max = (image_width * 0.15) * (image_height * 0.25) * 1.2
        return contour_area_min, contour_area_max

    def _get_arc_length_threshold(self, image_width, image_height):
        """
        计算轮廓弧长阈值

        :param image_width: 图像宽度
        :param image_height: 图像高度
        :return: 最小和最大弧长阈值
        """
        arc_length_min = ((image_width * 0.15) + (image_height * 0.25)) * 2 * 0.8
        arc_length_max = ((image_width * 0.15) + (image_height * 0.25)) * 2 * 1.2
        return arc_length_min, arc_length_max

    def _get_offset_threshold(self, image_width):
        """
        计算偏移量阈值

        :param image_width: 图像宽度
        :return: 最小和最大偏移量阈值
        """
        offset_min = 0.2 * image_width
        offset_max = 0.85 * image_width
        return offset_min, offset_max

    def _is_image_file(self, file_path: str) -> bool:
        """
        检查字符串是否是有效的图像文件路径
        """
        valid_extensions = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff')
        return os.path.isfile(file_path) and file_path.lower().endswith(valid_extensions)

    def _is_base64(self, s: str) -> bool:
        """
        检查字符串是否是有效的 base64 编码
        """
        try:
            if isinstance(s, str):
                # Strip out data URI scheme if present
                if "data:" in s and ";" in s:
                    s = s.split(",")[1]
                base64.b64decode(s)
                return True
            return False
        except Exception:
            return False

    def _read_image(self, image_source: Union[str, bytes], imread_flag: Optional[int] = None) -> np.ndarray:
        """
        读取图像

        :param image_source: 图像路径或base64编码
        :param imread_flag: cv2.imread 和 cv2.imdecode 的标志参数 (默认: None)
        :return: 读取的图像
        """
        if isinstance(image_source, str):
            if self._is_image_file(image_source):  # 如果是文件路径
                if imread_flag is not None:
                    return cv2.imread(image_source, imread_flag)
                else:
                    return cv2.imread(image_source)
            elif self._is_base64(image_source):  # 如果是 base64 编码
                # 剥离数据URI方案(如果存在)
                if "data:" in image_source and ";" in image_source:
                    image_source = image_source.split(",")[1]
                img_data = base64.b64decode(image_source)
                img_array = np.frombuffer(img_data, np.uint8)
                if imread_flag is not None:
                    image = cv2.imdecode(img_array, imread_flag)
                else:
                    image = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)
                if image is None:
                    raise ValueError("Failed to decode base64 image")
                return image
            else:
                raise ValueError("The provided string is neither a valid file path nor a valid base64 string")
        else:
            raise ValueError("image_source must be a file path or base64 encoded string")

    def get_slider_offset(self, background_source: Union[str, bytes], slider_source: Union[str, bytes],
                          out_file_name: str = None) -> int:
        """
        获取滑块的偏移量

        :param background_source: 背景图像路径或base64编码
        :param slider_source: 滑块图像路径或base64编码
        :param out_file_name: 输出图片的文件名: 默认为当前时间戳
        :return: 滑块的偏移量
        """
        background_image = self._read_image(background_source)
        slider_image = self._read_image(slider_source, cv2.IMREAD_UNCHANGED)
        out_file_name = out_file_name if out_file_name else datetime.now().strftime('%Y%m%d%H%M%S.%f')[:-3]

        if background_image is None:
            raise ValueError("Failed to read background image")

        if slider_image is None:
            raise ValueError("Failed to read slider image")

        slider_image_no_alpha = self._remove_alpha_channel(slider_image)
        image_height, image_width, _ = background_image.shape

        image_gaussian_blur = self._get_gaussian_blur_image(background_image)
        image_canny = self._get_canny_image(image_gaussian_blur)
        contours = self._get_contours(image_canny)

        if self.save_images:
            # 创建输出目录
            if not os.path.exists(self.output_path):
                os.makedirs(self.output_path)
            cv2.imwrite(os.path.join(self.output_path, f'{out_file_name}_image_canny.png'), image_canny)
            cv2.imwrite(os.path.join(self.output_path, f'{out_file_name}_image_gaussian_blur.png'), image_gaussian_blur)

        contour_area_min, contour_area_max = self._get_contour_area_threshold(image_width, image_height)
        arc_length_min, arc_length_max = self._get_arc_length_threshold(image_width, image_height)
        offset_min, offset_max = self._get_offset_threshold(image_width)

        offset = None
        for contour in contours:
            x, y, w, h = cv2.boundingRect(contour)
            if contour_area_min < cv2.contourArea(contour) < contour_area_max and \
                    arc_length_min < cv2.arcLength(contour, True) < arc_length_max and \
                    offset_min < x < offset_max:
                cv2.rectangle(background_image, (x, y), (x + w, y + h), (0, 0, 255), 2)
                offset = x

        # 匹配滑块模板在背景中的位置
        result = cv2.matchTemplate(background_image, slider_image_no_alpha, cv2.TM_CCOEFF_NORMED)
        _, _, _, max_loc = cv2.minMaxLoc(result)
        slider_x, slider_y = max_loc
        offset = slider_x

        cv2.rectangle(background_image, (slider_x, slider_y),
                      (slider_x + slider_image_no_alpha.shape[1], slider_y + slider_image_no_alpha.shape[0]),
                      (255, 0, 0), 2)

        if self.save_images:
            cv2.imwrite(os.path.join(self.output_path, f'{out_file_name}_image_label.png'), background_image)

        return offset

2.2 代码说明

  • 图像预处理:通过高斯模糊和Canny边缘检测增强图像的对比度和亮度,提高滑块识别率。
  • 多图像融合:通过多次处理图像并融合结果,以减小噪声对检测结果的影响。
  • 动态调整阈值:根据图像的直方图动态调整Canny边缘检测的阈值,提高对不同图像的适应性。
  • 轮廓检测:通过 _get_contours 函数获取图像的轮廓,并根据轮廓面积和弧长进行筛选。
  • 滑块匹配:通过模板匹配方法 cv2.matchTemplate 匹配滑块在背景图中的位置。

2.3 优化策略

  • 对比度和亮度增强:通过提高图像的对比度和亮度,使得滑块和背景的区别更加明显,增强滑块匹配的准确度。
  • 多图像融合:融合多张处理后的图像,减小单张图像中的噪声对结果的影响。
  • 动态调整参数:根据图像内容动态调整Canny边缘检测的阈值,使得算法对不同类型的图像都有较好的适应性。

2.4 安装依赖

要运行上述代码,需要安装以下 Python 库:

pip install numpy opencv-python slider_captcha_match

2.5 使用方法

在安装完所需库后,您可以按照以下步骤使用滑块验证码匹配功能:

  1. 初始化SliderCaptchaMatch类:配置高斯模糊、Canny边缘检测等参数。
  2. 读取背景图像和滑块图像:可以是文件路径或base64编码。
  3. 获取滑块偏移量:调用get_slider_offset函数,返回滑块的准确偏移量。
from slider_captcha_match import SliderCaptchaMatch

from datetime import datetime

import cv2

# 初始化 SliderCaptchaMatch 类

slider_captcha_match = SliderCaptchaMatch(save_images=True,output_path="output")

# 读取背景图像和滑块图像

background_source = "path_to_background_image.jpg"

slider_source = "path_to_slider_image.png"

# 获取滑块偏移量

offset = slider_captcha_match.get_slider_offset(background_source, slider_source)

print(f"滑块偏移量: {offset}")

# 输出结果保存路径

out_file_name = datetime.now().strftime('%Y%m%d%H%M%S.%f')[:-3]

print(f"结果图像保存路径: output/{out_file_name}_image_label.png")

三、测试与验证

为了验证优化后的滑块验证码匹配技术,进行多次测试,比较不同情况下的滑块偏移量检测结果,并记录背景图、滑块图、中间预处理图和代码标注的滑块位置的图,以及缺口坐标位置偏移量计算。

Response for row 1: offset(手动标注)=155;缺口坐标(代码计算)=155.0

 

Response for row 2: offset(手动标注)=119;缺口坐标(代码计算)=118.5


Response for row 2: offset(手动标注)=223;缺口坐标(代码计算)=224.0

四、总结

本文介绍了基于图像处理的滑块验证码匹配技术,并通过多种优化策略提高了滑块位置偏移量的检测准确度。通过对图像进行预处理、融合多张图像、动态调整阈值等方法,可以有效提高滑块验证码在不同背景下的识别率。希望这篇文章能够对从事图像处理和验证码研究的读者有所帮助。

参考资料

  1. OpenCV 官方文档
  2. NumPy 官方文档
  3. 本Github项目源码地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/777039.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

R语言fastshap包进行支持向量机shap可视化分析

1995年VAPINK 等人在统计学习理论的基础上提出了一种模式识别的新方法—支持向量机 。它根据有限的样本信息在模型的复杂性和学习能力之间寻求一种最佳折衷。 以期获得最好的泛化能力.支持向量机的理论基础决定了它最终求得的是全局最优值而不是局部极小值,从而也保证了它对未知…

在AvaotaA1全志T527开发板上使用AvaotaOS 部署 Docker 服务

Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows操作系统的机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口。 准备…

Maven 分模块设计与开发 继承

介绍 在 Maven 中进行分模块设计&#xff08;multi-module project&#xff09;&#xff0c;可以帮助将一个大型项目分解为更小、更易管理的模块。这种设计方式有助于提高项目的可维护性、复用性和团队协作效率。 继承关系 目录结构 引入父Maven 父坐标 在子项目中引入父亲…

雷电模拟器报错remount of the / superblock failed: Permission denied remount failed

报错截图 解决方法 打开设置 设置配置system.vmdk可写入 解决

【Nginx】docker运行Nginx及配置

Nginx镜像的获取 直接从Docker Hub拉取Nginx镜像通过Dockerfile构建Nginx镜像后拉取 二者区别 主要区别在于定制化程度和构建过程的控制&#xff1a; 直接拉取Nginx镜像&#xff1a; 简便性&#xff1a;直接使用docker pull nginx命令可以快速拉取官方的Nginx镜像。这个过程…

可变参数 Collections 不可变集合 Stream流

目录 1.可变参数&#xff1a; 2.Collections: 3.不可变集合&#xff1a; 4.Stream流: 1、什么是流 2、如何生成流 1.单列集合获取Stream流 2.双列集合获取Stream流 3.数组获取Stream流&#xff1a; 4.一堆零散数据&#xff1a; Stream接口中的静态方法 3.Stream流的…

使用友元函数访问私有数据

如果在本类以外的其他地方定义了一个函数&#xff08;这个函数可以是不属于任何类的非成员函数&#xff0c;也可以是其他类的成员函数&#xff09;&#xff0c;在类体中用friend对其进行声明&#xff0c;此函数就称为本类的友元函数。友元函数可以访问这个类中的私有成员。正如…

数据结构(3.5)——队列的顺序实现

队列的顺序实现 #define MaxSize 10//定义队列中元素的最大个数 typedef struct {int data[MaxSize];//用静态数组存放队列元素int front, rear;//队头指针和队尾指针 } SqQueue;void testQueue() {SqQueue Q;//声明一个队列(顺序存储) } 队列的初始化操作和判空 //初始化队…

昇思25天学习打卡营第11天 | LLM原理和实践:基于MindSpore实现BERT对话情绪识别

1. 基于MindSpore实现BERT对话情绪识别 1.1 环境配置 # 实验环境已经预装了mindspore2.2.14&#xff0c;如需更换mindspore版本&#xff0c;可更改下面mindspore的版本号 !pip uninstall mindspore -y !pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore2.2…

Rust变量绑定

变量绑定 Rust 通过静态类型确保类型安全。变量绑定可以在声明时说明类型&#xff0c;不过在多数情况下&#xff0c;编译器能够从上下文推导出变量的类型&#xff0c;从而大大减少了类型说明的工作。 使用 let 绑定操作可以将值&#xff08;比如字面量&#xff09;绑定&#…

ESP32 通过蓝牙显示歌词代码示例

通过蓝牙协议播放音乐&#xff0c;有的时候需要显示歌词&#xff0c;这里就是a2dp库获取了歌词 值得注意的是要想正确获取到歌词&#xff0c;必须打开各种播放器的字幕&#xff08;歌词&#xff09;开关 本项目用了三个开源库 a2dp&#xff0c;tft_espi,xfont. a2dp &#x…

ITWin Capture Modeler:打造卓越的软件模型的终极工具

在软件开发和设计领域&#xff0c;寻找一款高效且功能强大的软件模型工具是每个开发人员的追求。而经过多年的实践和尝试&#xff0c;我终于找到了一款令人印象深刻的工具——ITWin Capture Modeler。它不仅具备出色的功能和灵活性&#xff0c;而且能够极大地提高开发效率和质量…

计算机网络体系结构详解:协议与分层

在学习计算机网络时&#xff0c;理解网络协议与分层体系结构是至关重要的。本文将详细介绍这些概念&#xff0c;帮助基础小白快速入门。 1. 什么是网络协议 网络协议是计算机网络中用于数据交换的规则和标准。这些规则规定了数据格式、时序以及发送和接收数据时的动作。网络协…

数学不好能搞人工智能吗?

很遗憾&#xff0c;不能。 人工智能&#xff08;AI&#xff09;实际上是一个将数学、算法理论和工程实践紧密结合的领域。AI 扒开来看就是算法&#xff0c;也就是数学、概率论、统计学、各种数学理论的体现。 新的时代&#xff0c;程序员想要跨入 AI 之门&#xff0c;只要稍微…

FTP、http 、tcp

HTTP VS FTP HTTP &#xff1a;HyperText Transfer Protocol 超文本传输协议&#xff0c;是基于TCP协议 FTP&#xff1a; File Transfer Protocol 文件传输协议&#xff0c; 基于TCP协议&#xff0c; 基于UDP协议的FTP 叫做 TFTP HTTP 协议 通过一个SOCKET连接传输依次会话数…

奇舞周刊第532期:奇舞团生日快乐~

时光荏苒&#xff0c;岁月如歌&#xff0c;转眼间&#xff0c;奇舞团13岁啦&#x1f382;&#x1f382;&#x1f382;《奇舞周刊》也陪伴大家来到了第532期。&#x1f44f;&#x1f44f; 致敬每一位读者和创作者&#xff0c;是你们的热情、陪伴和鼓励&#xff0c;让我们不断前进…

【Linux】:进程创建与终止

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关Linux程序地址空间的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从…

基于java+springboot+vue实现的图书商城管理系统(文末源码+Lw)283

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本图书商城管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信…

java Web 优秀本科毕业论文系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 优秀本科毕业论文系统是一套完善的web设计系统&#xff0c;对理解JSP java serlvet 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&a…

【深度学习】图形模型基础(5):线性回归模型第三部分:线性回归模型拟合

1.引言 本博文专辑的焦点主要集中在回归模型的实用案例和工具上&#xff0c;从简单的单变量线性回归入手&#xff0c;逐步过渡到包含多个预测变量、非线性模型&#xff0c;以及在预测和因果推断中的应用。本文我们将介绍回归模型推断的一些数学结构&#xff0c;并提供一些代数…