【项目】基于YOLOv8和RotNet实现圆形滑块验证码(拼图)自动识别(通过识别中间圆形的角度实现)

news2024/11/13 8:54:46

@TOC

一、引言

1.1 实现目标

要达到的效果是使用算法预测中间圆形的角度,返回给服务器,实现自动完成验证码的问题。要实现的内容如下图所示。
在这里插入图片描述

在这里插入图片描述

1.2 实现思路

思路1(效果较差):以RotNet要实现的验证码识别为灵感,先利用YOLO算法检测出中间圆,再把圆形图像输入给RotNet,让其预测角度,进而返回给服务器。

但是实际应用的过程中,笔者发现这种算法逻辑执行效果较差。
因为RotNet要完成的是独立的圆预测角度,实现的是如下图所示的圆的角度预测,单独的圆就已经是独立出来的一张图像,因此直接输入进网络,预测效果会很好
在这里插入图片描述
但是我们要实现的相当于中间圆和外部图形的拼图操作,而不仅仅是简单的预测角度,所以直接把中间的圆拿出来进行角度预测,显然脱离了背景,而且有些题把中间独立的圆抠出来之后,很难对其角度进行定义,所以效果很差
在这里插入图片描述

思路2:有了思路1的教训,我们要做的第一步就是把外部的背景图像引入进来进行训练

二、数据集制备

网络上没有此类开源的数据集,因此笔者自行进行了制备,具体分为以下两种:

  1. 一种是以下这种圆完全归位的一整张图,都是用美工P图的方法进行制备的在这里插入图片描述
  2. 第二种是直接截取的这种没有P图过的没有修正过的图像
    在这里插入图片描述
    此类数据集制备完成之后再用笔者编写的脚本使其归位,部分代码如下
    大致思路为:
  • 先使用YOLO算法检测出图像中的圆
  • 再利用算法使用a、d两个按键进行角度偏转,使用z、c两个按键进行切图
  • 观察到图像回正之后,按s键保存到指定文件夹下

效果如下:

3eb50dabc910b6

import math
import numpy as np
import cv2
from ultralytics import YOLO
import os

yolo_model = YOLO(r"D:\kb\rotate-captcha-crack-master_my\yolo.pt")

def code_dect(folder_path, output_path):
    files = []
    current_index = 0

    while True:
        if not files:
            print("文件夹中没有图像文件。")
            break

        img_path = os.path.join(folder_path, files[current_index])
        img = cv2.imread(img_path)
        imgDoub = img
        # 检测出的box
        center_box = []
        results = yolo_model.predict(img, stream=True)
        boxAll = []
        for r in results:
            boxes = r.boxes
            for box in boxes:
                x1, y1, x2, y2 = box.xyxy[0]  # 获取边界框的坐标
                x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
                w, h = x2 - x1, y2 - y1
                bbox = (x1, y1, w, h)
                boxAll.append(bbox)
        # 计算图像中心坐标
        width, height = img.shape[0], img.shape[1]
        image_center_x = width / 2
        image_center_y = height / 2
        # 找出距离图像中心最近的矩形框
        min_distance = float('inf')

        for rectangle in boxAll:
            center_x, center_y = calculate_center(rectangle)
            distance = math.sqrt((center_x - image_center_x) ** 2 + (center_y - image_center_y) ** 2)
            if distance < min_distance:
                min_distance = distance
                center_box = rectangle

        if center_box == []:
            print(f"没有检测到目标:{img_path}")
        else:
            # 创建与图像相同大小的黑色背景
            mask = np.zeros_like(imgDoub[:, :, 0])
            # 定义圆的外接矩形坐标
            x, y, w, h = center_box
            # 在掩码上绘制白色的圆形
            cv2.circle(mask, (x + w // 2, y + h // 2), min(w, h) // 2, (255, 255, 255), -1)
            # 将掩码应用到白色背景上,保留圆形区域
            onlyCircle = cv2.bitwise_and(imgDoub, imgDoub, mask=mask)
            mask2 = np.zeros_like(img, dtype=np.uint8)
            # 在掩码上绘制圆形区域
            cv2.circle(mask2, (x + w // 2, y + h // 2), min(w, h) // 2, (255, 255, 255), -1)

            rotate = True
            reverse = False
            angle = 0

            while rotate:
                # 获取图像的中心点坐标
                height, width = onlyCircle.shape[:2]
                center = (width // 2, height // 2)
                # 定义旋转矩阵
                rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
                # 进行旋转变换
                rotated_image = cv2.warpAffine(onlyCircle, rotation_matrix, (width, height))
                # 将圆形区域置为0
                imgDoub[mask2 != 0] = 0
                result = rotated_image + imgDoub
                cv2.imshow("result", result)

                # 添加按键监听
                key = cv2.waitKey(1)

                # z、c分别是切换上一张或者下一张图
                if key == ord('z'):
                    current_index = (current_index - 1) % len(files)
                    break
                # 按下 'c' 键逆时针旋转
                elif key == ord('c'):
                    current_index = (current_index + 1) % len(files)
                    break
                # 按下 's' 键保存图像
                elif key == ord('s'):
                    output_img_path = os.path.join(output_path, files[current_index])
                    cv2.imwrite(output_img_path, result)
                    print(f"图像已保存到:{output_img_path}")
                if key == ord('a'):
                    angle += 1
                    # 按下 'd' 键逆时针旋转
                elif key == ord('d'):
                    angle -= 1
                elif key == ord('q'):
                    angle += 10
                elif key == ord('e'):
                    angle -= 10


if __name__ == '__main__':
    folder_path = input("请输入文件夹路径:")
    output_path = input("请输入输出路径:")
    code_dect(folder_path, output_path)

三、算法逻辑

1、生成样本

使用RotNet作为本算法的预测核心预测算法,把我们上文中生成的回正数据首先利用编写的脚本给每张图像生成360张不同角度的图像,文件名的后缀代表这张图象真实的偏转角度。
,

2、训练算法

把生成的所有图像输入进改进的RotNet进行训练,由于这种类型的样本学习很容易出现过拟合的现象,因此笔者在网络中加了几个DropOut操作。

3、算法逻辑

我们并没有简单把单张图像输入进算法来进行角度预测,这样360个类别误差太大效果会比较差,在应用的时候我们也是先把中间的圆形图像抠出来,然后对其使用算法旋转360度,把360张图像都进行角度预测,最后取出0到3度和357到359度的图像返回它的序列值,即真实的角度值。如果没有这些范围之内的图像,那就返回-1,切下一张图像,防止错误次数太多。代码如下所示:

import math
import numpy as np
import torch
from PIL import Image
from rotate_captcha_crack.common import device
from rotate_captcha_crack.model import RotNetR
from rotate_captcha_crack.utils import process_captcha
import cv2
from ultralytics import YOLO

yolo_model = YOLO(r"D:\chenjie\rotate-captcha-crack-master_my\yolo.pt")

def calculate_center(rectangle):
    x, y, w, h = rectangle
    center_x = x + w / 2
    center_y = y + h / 2
    return center_x, center_y



model = RotNetR(train=False, cls_num=360)
model_path = r"D:\chenjie\rotate-captcha-crack-master_my\models\RotNetR\240316_17_14_23_006\best.pth"

model.load_state_dict(torch.load(str(model_path)))
model = model.to(device=device)
model.eval()
def predictAngle(img):
    img = Image.fromarray(img)
    img_ts = process_captcha(img)
    img_ts = img_ts.to(device=device)
    predict = model.predict(img_ts)
    return predict

def code_dect(img):
    imgDoub = img
    # 检测出的box
    center_box = []
    results = yolo_model.predict(img, stream=True)
    boxAll = []
    for r in results:
        boxes = r.boxes
        for box in boxes:
            x1, y1, x2, y2 = box.xyxy[0]  # Gives coordinates to draw bounding box
            x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
            w, h = x2 - x1, y2 - y1
            bbox = (x1, y1, w, h)
            boxAll.append(bbox)
    # 计算图像中心坐标
    width, height = img.shape[0], img.shape[1]
    image_center_x = width / 2
    image_center_y = height / 2
    # 找出距离图像中心最近的矩形框
    min_distance = float('inf')

    for rectangle in boxAll:
        center_x, center_y = calculate_center(rectangle)
        distance = math.sqrt((center_x - image_center_x) ** 2 + (center_y - image_center_y) ** 2)
        if distance < min_distance:
            min_distance = distance
            center_box = rectangle

    if center_box==[]:
        print("kong")
    else:
        # 把圆区域搞出来
        # 创建与图像相同大小的黑色背景
        mask = np.zeros_like(imgDoub[:, :, 0])
        # 定义圆的外接矩形坐标
        x, y, w, h = center_box
        # 在掩码上绘制白色的圆形
        cv2.circle(mask, (x + w // 2, y + h // 2), min(w, h) // 2, (255, 255, 255), -1)
        # 将掩码应用到白色背景上,保留圆形区域
        onlyCircle = cv2.bitwise_and(imgDoub, imgDoub, mask=mask)
        # 显示结果图像
        # cv2.imshow('Only Circle', onlyCircle)
        # cv2.imshow('imgDoub', imgDoub)
        # cv2.waitKey(0)
        mask2 = np.zeros_like(img, dtype=np.uint8)
        # 在掩码上绘制圆形区域
        cv2.circle(mask2, (x + w // 2, y + h // 2), min(w, h) // 2, (255, 255, 255), -1)
        angles = []
        for angle in range(0, 360):
            # 获取图像的中心点坐标
            height, width = onlyCircle.shape[:2]
            center = (width // 2, height // 2)
            # 定义旋转矩阵
            rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
            # 进行旋转变换
            rotated_image = cv2.warpAffine(onlyCircle, rotation_matrix, (width, height))
            # 将圆形区域置为0
            imgDoub[mask2 != 0] = 0
            result = rotated_image+imgDoub
            predict = predictAngle(result)
            # cv2.imshow("sdf",result)
            # print(predict)
            # cv2.waitKey(10)
            angles.append(predict)
        minAngle = min(angles)
        maxAngle = max(angles)
        minAngleIdx = angles.index(min(angles))
        maxAngleIdx = angles.index(max(angles))
        finalAngleIdx = -1

        if maxAngle>356:
            finalAngleIdx = maxAngleIdx
        elif minAngle<4:
            finalAngleIdx = minAngleIdx
        print(finalAngleIdx)
        return finalAngleIdx


if __name__ == '__main__':
    # 测试整体
    img = cv2.imread("D:\chenjie\\rotate-captcha-crack-master_my\images\e42c0939dc3bde88657a88ac07d59d6.png")
    code_dect(img)

最后可以达到80-90%的通过率,效果已经很不错了
演示效果如下:

ab7c1b94c3bc27b8

三、代码、数据集获取

q:1831255794(有偿)制备数据集和写算法耗费了大量时间精力,因此收取点小费希望理解!!!
可接项目,大作业,毕设等 
价格略贵,技术够硬,认真负责,保证质量

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【技术栈】Redis 的理解与数据存储格式

SueWakeup 个人主页&#xff1a;SueWakeup 系列专栏&#xff1a;学习技术栈 个性签名&#xff1a;保留赤子之心也许是种幸运吧 本文封面由 凯楠&#x1f4f8; 友情提供 目录 相关传送门 1. NOSQL和关系型数据库比较 2. 主流的NOSQL产品 3. Redis的理解 4. redis数据存储格式…

Golang标准库fmt深入解析与应用技巧

Golang标准库fmt深入解析与应用技巧 前言fmt包的基本使用打印与格式化输出函数Print系列函数格式化字符串 格式化输入函数小结 字符串格式化基本类型的格式化输出自定义类型的格式化输出控制格式化输出的宽度和精度小结 错误处理与fmt使用fmt.Errorf生成错误信息fmt包与错误处理…

vue/vite添加地图

最简单的方式&#xff0c;不论vue2、vue3、vite均适用&#xff0c;例如以高德为例&#xff1a; index.html 引入 <scriptsrc"https://webapi.amap.com/maps?v1.4.15&key您的key&pluginAMap.ToolBar,AMap.MouseTool,AMap.DistrictSearch,AMap.ControlBar&quo…

filezilla客户端的应用以及ftplftpwget的用法

filezilla的应用 用户的配置查看上一篇文章FTP3种用户的配置 进入filezilla软件测试 用yy用户登录发现可以上传下载创建删除 再用cc用户登录发现不能上传不能删除不能创建只能下载 ftp&lftp&wget客户端的应用 以命令行的方式连接ftp&#xff0c;一般只会用到上…

MAC废纸篓删掉还能复原吗 MAC废纸篓倾倒掉的文件怎么恢复 删除的东西在哪里可以找回来 怎么找回已删除的文件

MAC系统中的废纸篓&#xff08;Trash&#xff09;通常指用来临时存放用户即将丢弃的文件的地方。MAC系统的废纸篓功能相当于Windows系统的垃圾回收站&#xff0c;通过废纸篓删除的文件&#xff0c;一般是无法从系统中操作还原。那么&#xff0c;MAC废纸篓删掉还能复原吗&#x…

Linux系统(四)- 进程初识 | 环境变量 | 进程地址空间

~~~~ 前言冯诺依曼体系结构&#xff08;重要&#xff09;总览CPU工作方式什么是指令集&#xff1f;CPU为什么只和内存打交道&#xff08;数据交换&#xff09;&#xff1f;木桶效应&#xff1a;在数据层面的结论程序运行为什么要加载到内存&#xff1f; 进一步理解计算机体系结…

应用APM-如何配置Prometheus + Grafana监控springboot应用

文章目录 概述在Spring Boot应用中集成Micrometerspringboot配置修改 Docker安装Prometheus和Grafanaprometheus配置grafana配置启动Prometheus和Grafana在Grafana中配置数据源创建Grafana仪表盘配置Grafana告警&#xff08;可选&#xff09;监控和分析 概述 配置Prometheus和…

NASA数据集——2017 年来自 Arctic-CAP 的大气中 CO、CO2 和 CH4 浓度剖面图

简介 ABoVE: Atmospheric Profiles of CO, CO2 and CH4 Concentrations from Arctic-CAP, 2017 文件修订日期&#xff1a;2019-05-01 数据集版本: 1 数据集摘要 本数据集提供了 2017 年 4 月至 11 月北极碳飞机剖面&#xff08;Arctic-CAP&#xff09;月度采样活动期间阿拉…

MIT的研究人员最近开发了一种名为“FeatUp”的新算法,这一突破性技术为计算机视觉领域带来了高分辨率的洞察力

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【冥想X理工科思维】场景10:长期项目的焦虑和压力

冥想音频合集&#xff1a;职场解压冥想音频 压力场景&#xff1a; 在长期项目中&#xff0c;如何定期冥想帮我保持耐心和持久性&#xff1f; 点击看大图&#xff1a; 详细说明&#xff1a;通过这个冥想流程&#xff0c;你可以帮助自己在长期项目中保持耐心、坚持和放松的状态。…

SAP前台处理:物料主数据创建<MM01>之会计视图

一、背景&#xff1a; 终于来到了物料主数据&#xff0c;我觉得物料账是SAP最重要的一项发明&#xff0c;也一直是SAP的一项重要优势&#xff0c;物料账记录了一个个物料的生生不息&#xff1b; 本章主要讲解物料主数据和财务相关的主要内容&#xff1a;这里特别提示由于作者…

类和对象-4

文章目录 前言const成员函数取地址及const取地址操作符重载构造函数续explicit static成员友元内部类匿名对象 前言 在前面的文章中&#xff0c;我们了解到了类的四个默认成员函数&#xff1a;构造、析构、拷贝构造和赋值重载。接下来我们会继续学习剩下的两个默认成员函数以及…

CAD建筑版2024 安装教程

CAD建筑版是一种专门用于建筑设计和绘图的CAD软件版本。它提供了专业的建筑设计工具和功能&#xff0c;帮助建筑师、设计师和工程师在建筑领域进行快速、准确和高效的设计工作。 CAD建筑版具备建筑相关的库和元素&#xff0c;用户可以方便地使用预定义的建筑符号和元素进行建筑…

二叉树|104.二叉树的最大深度 111.二叉树的最小深度

104.二叉树的最大深度 力扣题目链接 class solution { public:int getdepth(TreeNode* node) {if (node NULL) return 0;int leftdepth getdepth(node->left); // 左int rightdepth getdepth(node->right); // 右int depth 1 max(leftdepth, rightdepth…

嵌入式-4种经典继电器驱动电路-单片机IO端口/三极管/达林顿管/嵌套连接

文章目录 一&#xff1a;继电器原理二&#xff1a;单片机驱动电路三&#xff1a;经典继电器驱动电路方案3.1 继电器驱动电路方案一&#xff1a;I/O端口灌电流方式的直接连接3.1.1 方案一的继电器特性要求3.1.2 方案一可能会损坏I/O口 3.2 继电器驱动电路方案二&#xff1a;三极…

记录一次基于AES加密的恶意软件逆向分析和解密过程(含文件)

导入(Imports)和字符串 首先,用IDA或Ghidra加载文件test.dat,文件为64位文件 IDA点击View==>Open subviews==>Imports,查看导入信息 Ghidra可以直接看到 可以看到,导入函数有: __printf_chk, malloc, __isoc99_sscanf, putchar, __stack_chk_fail, __cxa_fina…

安捷伦Agilent E8361C网络分析仪

181/2461/8938产品概述&#xff1a; 安捷伦e 8361 c网络分析仪提供通用网络分析&#xff0c;可选软件和/或硬件为您的应用定制-如多端口、脉冲射频等。 安捷伦E8361C网络分析仪的显示窗口数量不限&#xff0c;可以调整大小和重新排列&#xff0c;每个窗口最多有24条活动轨迹和…

CSS 脱离标准文档流 浮动

浮动 在标准流当中&#xff0c;元素或者标签在页面上摆放的时候会出现不如意的地方。要想解决这些问题可以采用脱离标准流的方式来进行解决这些问题&#xff0c;脱离标准流也称为脱离文档流。 脱离标准流的解决方式有三种&#xff0c;一种是浮动&#xff0c;另外一种是固定定位…

【Flask】Flask项目结构初识

1.前提准备 Python版本 # python 3.8.0 # 查看Python版本 python --version 安装第三方 Flask pip install flask # 如果安装失败&#xff0c;可以使用 -i&#xff0c;指定使用国内镜像源 # 清华镜像源&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple/ 检查 Flask 是…

大数据分析-基于Python的电影票房信息数据的爬取及分析

概要 现如今&#xff0c;人民群众对物质生活水平的要求已不再局限于衣食住行&#xff0c;对于精神文化有了更多的需求。电影在我国越来越受欢迎&#xff0c;电影业的发展越来越迅猛&#xff0c;为了充分利用互联网技术的发展&#xff0c;掌握电影业的态势&#xff0c;对信息进行…