rotate-captcha-crack项目重新训练百度旋转验证码角度预测模型

参考:

building-powerful-image-classification-models-using-very-little-data.html

https://github.com/Starry-OvO/rotate-captcha-crack (主)作者思路:https://www.52pojie.cn/thread-1754224-1-1.html
在这里插入图片描述
纠正 新版百度、百家号旋转验证码识别

d4net作者博客

训练

图片来源

角度为0的百度验证码图片
可以先爬虫获取多张,然后计算相似度删除重复图片

训练数据集规模

一张验证码可以复制多次(具体多少 看你底图数据量),这样一个epoch内会出现多个角度,模型快速学习,方便收敛

一张验证码不进行多次复制,验证集loss震荡会很厉害,300张图片loss可以降低到1.4 ,但是泛化性也很差
在这里插入图片描述
在这里插入图片描述

加载原始模型代码

(rotate-captcha-crack原始加载模型方法可能报错,所以重写)

def fineturn_from_old_model(self):model_path = r'models/RotNetR/230308_08_02_34_000/best.pth'print(model_path,'------------------------------------------')state_dict = torch.load(model_path, map_location=torch.device('cuda'))model = RotNetR(180)model = model.to(device)model.load_state_dict(state_dict)# 冻结除最后一层之外的所有层for name, param in model.named_parameters():if not name.startswith('backbone.fc'):  # fc为最后一层的名称param.requires_grad = Falseelse:print(name)self.model.load_state_dict(model.state_dict())

数据增强办法

rotate_captcha_crack\dataset\rot.py 中 内容如下:
使用的数据增强办法 :

  • 改变图像颜色的四个方面:亮度、对比度、饱和度和色调
  • 随机剪裁
  • 左右翻转
from typing import Tupleimport torch
from torch import Tensor
from torch.utils.data import Dataset
from torchvision.transforms import Normalize
from torchvision import transformsfrom ..const import DEFAULT_CLS_NUM, DEFAULT_TARGET_SIZE
from .helper import DEFAULT_NORM, from_img
from .typing import TypeImgTsSeqTypeRotItem = Tuple[Tensor, Tensor]class RotDataset(Dataset[TypeRotItem]):"""Dataset for RotNet (classification).Args:imgseq (TypeImgSeq): upstream datasettarget_size (int, optional): output img size. Defaults to `DEFAULT_TARGET_SIZE`.norm (Normalize, optional): normalize policy. Defaults to `DEFAULT_NORM`.Methods:- `def __len__(self) -> int:` length of the dataset- `def __getitem__(self, idx: int) -> TypeRotItem:` get square img_ts and index_ts([C,H,W]=[3,target_size,target_size], dtype=float32, range=[0.0,1.0)), ([N]=[1], dtype=long, range=[0,cls_num))"""__slots__ = ['imgseq','cls_num','target_size','norm','size','indices',]def __init__(self,imgseq: TypeImgTsSeq,cls_num: int = DEFAULT_CLS_NUM,target_size: int = DEFAULT_TARGET_SIZE,norm: Normalize = DEFAULT_NORM,) -> None:self.imgseq = imgseqself.cls_num = cls_numself.target_size = target_sizeself.norm = normself.size = self.imgseq.__len__()self.indices = torch.randint(cls_num, (self.size,), dtype=torch.long)self.transforms = transforms.Compose([# transforms.Resize(240), # 将图像最短边缩至240,宽高比例不变transforms.RandomHorizontalFlip(), # 以0.5的概率左右翻转图像# transforms.ToTensor(), # 将PIL图像转为Tensor,并且进行归一化# transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) # 进行mean与std为0.5的标准化transforms.RandomResizedCrop( (350, 350), scale=(0.8, 1), ratio=(0.5, 2)),# 随机裁剪一个面积为原始面积50%100%的区域,该区域的宽高比从0.52之间随机取值。 然后,区域的宽度和高度都被缩放到350像素。# 我们可以改变图像颜色的四个方面:亮度、对比度、饱和度和色调transforms.ColorJitter( brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5) ])def __len__(self) -> int:return self.sizedef __getitem__(self, idx: int) -> TypeRotItem:img_ts = self.imgseq[idx] #tensorimg_ts = self.transforms(img_ts)index_ts: Tensor = self.indices[idx] # 旋转44img_ts = from_img(img_ts, index_ts.item() / self.cls_num, self.target_size)img_ts = self.norm(img_ts)return img_ts, index_ts

tensor类型图片保存方法

from torchvision import transforms
toPIL= transforms.ToPILImage()  # 这个函数可以将张量转为PIL图片,由小数转为0-255之间的像素值
pic = toPIL(img_ts2)
pic.save('img_ts2.jpg')

训练过程和结果

结果:
一开始使用原始底图110张图片 准确率有80%(感谢simple ocr项目拥有者提供图片),数据增强后可以达到85%准确率,训练集loss可以降低到1.2,python test_RotNetR.py 计算平均误差度数的结果达到1度以内
在这里插入图片描述

后续训练, 增加训练图片到300张(保证图片质量较高,多样性丰富),准确度可以达到90%以上

数据预处理

找出相似的图片,先去重

 
import numpy as np
import os
import cv2
from tqdm import tqdm
def ssim(y_true , y_pred):u_true = np.mean(y_true)u_pred = np.mean(y_pred)var_true = np.var(y_true)var_pred = np.var(y_pred)std_true = np.sqrt(var_true)std_pred = np.sqrt(var_pred)R = 255c1 = np.square(0.01*R)c2 = np.square(0.03*R)ssim = (2 * u_true * u_pred + c1) * (2 * std_pred * std_true + c2)denom = (u_true ** 2 + u_pred ** 2 + c1) * (var_pred + var_true + c2)return ssim / denomdef show(image1,image2=''):# 创建一个窗口并显示合并后的图片if image2=='':combined_image =image1else:combined_image = cv2.hconcat([image1, image2])cv2.namedWindow('Combined Image', cv2.WINDOW_NORMAL)cv2.imshow('Combined Image', combined_image)# 等待用户按下任意键,然后关闭窗口cv2.waitKey(0)cv2.destroyAllWindows()# 获取当前文件夹中的所有图片文件路径
image_folder = r'xxx'  # 存放图片的文件夹路径file_list = os.listdir(image_folder)
result_file_list = []
images = sorted([os.path.join(image_folder, file) for file in file_list if file.endswith(('jpg', 'png', 'jpeg'))])
doubt_list = []for i in range(len(images)):print(i)r_list = []max_r = 0max_j = 0img1 = cv2.imread(images[i] )# 灰度图像处理gray_img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)# _, b_img1 = cv2.threshold(gray_img1, 200, 255, cv2.THRESH_BINARY)# ret, img1 = cv2.threshold(img1, 127, 255, cv2.THRESH_BINARY)for j in range(i+1,len(images)):flag = 0try:img2 = cv2.imread(images[j])gray_img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)# _, b_img2 = cv2.threshold(gray_img2, 180, 255, cv2.THRESH_BINARY)r = ssim(gray_img1, gray_img2)if r > 0.99 and r > max_r:max_r = rmax_j = jexcept:print('图片已经删除')if max_j!=0 and max_r>0.99:# show(b_img1, b_img2)result_file_list.append(images[i])cv2.imwrite('%s_first.jpg'%(i),img1)cv2.imwrite('%s_%s_second.jpg'%(i,max_j), cv2.imread(images[max_j]) )# show(img1, cv2.imread(images[max_j]))## if flag == 1:#     doubt_list.append([file_list[i], file_list[j]])
print(result_file_list)#类似('101_164.png', '126_216.png') ('130_112.png', '99_172.png')

修正图片角度

注意旋转图片会造成图片质量降低,使用cv2.INTER_CUBIC填充,之后还可以使用高分辨率工具恢复图像

在这里插入图片描述

from flask import Flask, render_template, request
from PIL import Image
import os
import cv2
import numpy as np
from PIL import Image, ImageOps
app = Flask(__name__)# 获取当前文件夹中的所有图片文件路径
image_folder = 'static/images'  # 存放图片的文件夹路径images_path = sorted([os.path.join(image_folder, file) for file in os.listdir(image_folder) if file.endswith(('jpg', 'png', 'jpeg'))])
current_index = 0  # 当前显示的图片索引def get_current_index_by_name(file_name):global images_pathresult_index_list = [i for i in range(len(images_path)) if os.path.basename(images_path[i])==file_name]return result_index_list[0]def rotate_image( image, angle, if_fill_white = True):'''顺时针旋转Args:image:angle:if_fill_white:旋转产生的黑边填充为白色Returns:'''# dividing height and width by 2 to get the center of the imageheight, width = image.shape[:2]# get the center coordinates of the image to create the 2D rotation matrixcenter = (width / 2, height / 2)# using cv2.getRotationMatrix2D() to get the rotation matrixrotate_matrix = cv2.getRotationMatrix2D(center=center, angle=angle, scale=1)# rotate the image using cv2.warpAffineif not if_fill_white:rotated_image = cv2.warpAffine(src=image, M=rotate_matrix, dsize=(width, height), flags=cv2.INTER_CUBIC)else:color = (255, 255) if len(image.shape)==2 else (255, 255,255)rotated_image = cv2.warpAffine(src=image, M=rotate_matrix, dsize=(width, height), borderValue=color, flags=cv2.INTER_CUBIC)return rotated_image# 显示当前图片
@app.route('/')
def index():global current_indeximg_path = images_path[current_index]return render_template('index.html', img_path=img_path)def cv_imread(file_path):cv_img = cv2.imdecode(np.fromfile(file_path,dtype=np.uint8),-1)return cv_imgdef cv_imwrite(img_path, img):cv2.imencode('.jpg', img)[1].tofile(img_path)# 处理图片切换和旋转请求
@app.route('/action', methods=['POST'])
def handle_action():global current_index,images_pathimg_path = images_path[current_index] #原图路径action = request.form['action']degree = 1 if request.form['input_text']=='' else float(request.form['input_text'])# rotated_img = Image.open(img_path)rotated_img = cv_imread(img_path)if action == 'left':rotated_img = rotate_image(rotated_img, degree)cv_imwrite(img_path,rotated_img)# rotated_img = rotated_img.rotate(1, expand=False, fillcolor='white')# rotated_img.save(img_path, quality=95)elif action == 'right':rotated_img = rotate_image(rotated_img, -degree)cv_imwrite(img_path, rotated_img)# rotated_img = rotated_img.rotate(-1, expand=False, fillcolor='white')# rotated_img.save(img_path, quality = 95)else:if action == 'next':current_index = (current_index + 1) % len(images_path)elif action == 'previous':current_index = (current_index - 1) % len(images_path)return render_template('index.html', img_path= images_path[current_index] )if __name__ == '__main__':app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Image Viewer</title><style>.container {text-align: center;position: relative; /* 设置相对定位 */}.image-container {display: inline-block;text-align: left;position: relative; /* 设置相对定位 */}.image-container img {max-width: 50%; /* 调整图片最大宽度为 80% */height: auto;margin: 10px;}.button-container {margin-top: 20px;}/* 模拟九宫格水平仪 */.nine-grid {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);width: 80%; /* 九宫格宽度 */height: 80%; /* 九宫格高度 */border: 1px dashed red; /* 红色虚线边框 */}/* 水平线 */.nine-grid::before,.nine-grid::after,.nine-grid::first-line {content: "";position: absolute;background-color: red; /* 红色实线 */width: 100%;height: 1px; /* 水平线粗细 */}/* 垂直线 */.nine-grid::before,.nine-grid::after,.nine-grid::first-line {content: "";position: absolute;background-color: red; /* 红色实线 */width: 1px; /* 垂直线粗细 */height: 100%;}/* 第一条水平线位置 */.nine-grid::before {top: 50%;left: 0;}/* 第二条水平线位置 */.nine-grid::first-line {top: 60%;left: 0;}/* 第三条水平线位置 */.nine-grid::after {top: 70%;left: 0;}/* 第一条垂直线位置 */.nine-grid::before {top: 0;left: 10%;}/* 第二条垂直线位置 */.nine-grid::first-line {top: 0;left: 25%;}/* 第三条垂直线位置 */.nine-grid::after {top: 0;left: 40%;}</style>
</head>
<body>
<div class="container"><div class="image-container"><div class="nine-grid"></div> <!-- 模拟横竖虚线水平仪 --><img src="{{ img_path }}" alt="Current Image"></div>{{ img_path }}<form action="/action" method="post"><div class="button-container"><button type="submit" name="action" value="previous">Previous Pic</button><input type="text" name="input_text"><button type="submit" name="action" value="left">Left</button><button type="submit" name="action" value="right">Right</button><button type="submit" name="action" value="next">Next Pic</button></div></form>
</div>
</body>
</html>

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

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

相关文章

低成本总线技术——LIN总线协议规范介绍

关注菲益科公众号—>对话窗口发送 “CANoe ”或“INCA”&#xff0c;即可获得canoe入门到精通电子书和INCA软件安装包&#xff08;不带授权码&#xff09;下载地址。 本篇文章主要介绍LIN总线协议规范。 数据帧的结构 LIN的数据帧包括报头&#xff0c;响应两大部分。而报头…

Visual Studio 2013 “即将退休”

新年快乐&#xff01; 这也是向各位开发者提醒 Visual Studio 支持生命周期中即将到来的好时机。 对 Visual Studio 2013 的支持即将在今年(2024年)的4月9日结束。如果你正在使用旧版本的 Visual Studio&#xff0c;我们强烈建议您升级您的开发环境到最新的 Visual Studio 20…

浏览器是如何渲染页面的

浏览器是个极其复杂的程序&#xff0c;这里只是挑几个和前端息息相关的重要内容来说 在学习如何渲染之前需要知道一个浏览器浏览器会有多个进程&#xff0c;其中主要进程有浏览器进程&#xff0c;网络进程&#xff0c;渲染进程这里我们主要学习内容就发生在渲染进程。当渲染进程…

【解决openGauss无法使用gs_check等服务器端命令问题】

【解决openGauss无法使用gs_check等服务器端命令问题】 一、问题描述二、问题原因三、解决方法 一、问题描述 [ommopengauss03 ~]$ gs_check -i CheckCPU Parsing the check items config file successfully [GAUSS-53026]: ERROR: Execute SSH command on host 192.168.56.19…

给出一句话来描述想要的图片,就能从图库中搜出来符合要求的

介绍 地址&#xff1a;https://github.com/mazzzystar/Queryable The open-source code of Queryable, an iOS app, leverages the OpenAIs CLIP model to conduct offline searches in the Photos album. Unlike the category-based search model built into the iOS Photos…

项目使用PowerJob

新一代的定时任务框架——PowerJob 简介 PowerJob是基于java开发的企业级的分布式任务调度平台&#xff0c;与xxl-job一样&#xff0c;基于web页面实现任务调度配置与记录&#xff0c;使用简单&#xff0c;上手快速&#xff0c;其主要功能特性如下&#xff1a; 使用简单&…

如何在 Windows 上从电脑硬盘恢复照片

如今&#xff0c;随着相机设备的普及&#xff0c;您可以轻松地一次拍摄一堆照片&#xff0c;将它们传输到硬盘上&#xff0c;然后再拍摄更多照片。但是&#xff0c;如果您的所有照片意外丢失在驱动器中怎么办&#xff1f;你能恢复它们吗&#xff1f; 在本指南中&#xff0c;我…

EasyRecovery2024永久免费版电脑数据恢复软件

EasyRecovery是一款操作安全、价格便宜、用户自主操作的非破坏性的只读应用程序&#xff0c;它不会往源驱上写任何东西&#xff0c;也不会对源驱做任何改变。它支持从各种各样的存储介质恢复删除或者丢失的文件&#xff0c;其支持的媒体介质包括&#xff1a;硬盘驱动器、光驱、…

postgresql可视化导入csv文件

不需要在命令行copy了&#xff0c;只需简单点几下 1.在数据库下建一个schema 右击选中数据库-new schema 2.双击你创建的schema&#xff0c;出现tables 3.右击tables&#xff0c;选择import wizard 4.选择你想导入的文件格式&#xff0c;之后一直next 5.选择你的文件所在位置…

C语言实用第三方库Melon开箱即用之多线程模型

在之前的文章中&#xff08;开发利器——C 语言必备实用第三方库&#xff09;&#xff0c;笔者介绍了一款Linux/UNIX下C语言库Melon的基本功能&#xff0c;并给出了一个简单的多进程开箱即用的例子。 本文将给大家介绍Melon中多线程的使用方法。 在Melon中有三种多线程模式&a…

Redis 之父锐评 LLM 编程:全知全能 Stupid|一周IT资讯

阿里通义千问上线“科目三”&#xff0c;刘皇叔、奥特曼、马斯克通通没逃过 在刚到的2024年&#xff0c;阿里通义千问 APP 上线图片生成舞蹈功能&#xff0c;用户只需输入一张图片&#xff0c;就能生成爆款舞蹈图片。 不管是“科目三”&#xff0c;还是鬼步舞、兔子舞&#x…

(18)Linux 实现简易版shell

前言&#xff1a;做一个 "会创建&#xff0c;会终止&#xff0c;会等待&#xff0c;会程序替换" 的简易 shell 。 1、显示提示符和获取用户输入 shell 本质就是个死循环&#xff0c;我们不关心获取这些属性的接口&#xff0c;如果要实现 shell&#xff1a; 1&…

015:JS之正则表达式,web乱码和路径问题总结,MVC架构模式

一 JS的正则表达式 1 正则表达式简介 正则表达式是描述字符模式的对象。正则表达式用于对字符串模式匹配及检索替换&#xff0c;是对字符串执行模式匹配的强大工具。 语法 var pattnew RegExp(pattern,modifiers);//正则的格式模版&#xff0c;修饰符或者更简单的方式:var pa…

权威测评首家通过!亚信安慧AntDB通过中国信通院数据库迁移工具专项测试

近日&#xff0c;亚信安慧数据库数据同步平台在中国信通院第17批“可信数据库”数据库迁移工具专项测试中&#xff0c;完全符合《数据库迁移工具能力要求》&#xff0c;成为首家通过标准测试的产品。这一成果标志着湖南亚信安慧科技有限公司&#xff08;简称“亚信安慧”&#…

Pandas透视表及应用

Pandas 透视表概述 数据透视表&#xff08;Pivot Table&#xff09;是一种交互式的表&#xff0c;可以进行某些计算&#xff0c;如求和与计数等。所进行的计算与数据跟数据透视表中的排列有关。 之所以称为数据透视表&#xff0c;是因为可以动态地改变它们的版面布置&#xf…

Git保姆级安装教程

Git保姆级安装教程 一、去哪下载二、安装2.1 具体安装步骤2.2 设置全局用户签名 一、去哪下载 1、官网&#xff08;有最新版本&#xff09;&#xff1a;https://git-for-windows.github.io/ 2、本人学习时安装的版本&#xff0c;链接&#xff1a;https://pan.baidu.com/s/1uAo…

Spark内核解析-数据存储5(六)

1、Spark的数据存储 Spark计算速度远胜于Hadoop的原因之一就在于中间结果是缓存在内存而不是直接写入到disk&#xff0c;本文尝试分析Spark中存储子系统的构成&#xff0c;并以数据写入和数据读取为例&#xff0c;讲述清楚存储子系统中各部件的交互关系。 1.1存储子系统概览 …

LabVIEW开发分布式光纤油气管道泄漏检测及预警系统

LabVIEW开发分布式光纤油气管道泄漏检测及预警系统 随着油气工业的发展&#xff0c;管道泄漏成为一个严峻的安全问题。本文介绍了一种基于LabVIEW的分布式光纤油气管道泄漏检测及预警系统的设计思路和组成结构。系统包括硬件和软件两部分&#xff0c;其中硬件部分详细阐述了分…

redis报错:Creating Server TCP listening socket 127.0.0.1:6379: bind: No error

Redis启动时报错&#xff1a; Creating Server TCP listening socket 127.0.0.1:6379: bind: No error 这个错误说明已经开启了redis&#xff0c;并且已经占用了端口6379&#xff0c;需要停止redis后再开启。 redis-cli.exeshutdownexitredis-server redis.windows.conf 参考…