首页 > 其他分享 >Tesserast-OCR踩坑记录——训练一个能识别验证码的OCR模型

Tesserast-OCR踩坑记录——训练一个能识别验证码的OCR模型

时间:2024-11-06 20:22:10浏览次数:3  
标签:训练 模型 self Tesserast 验证码 OCR image 图片

前言

公司项目的系统登录有一套验证码系统,之前想写一些自动化测试时总是会被这个验证码卡住,不能完全自动运行。去找开发同事关一下验证码,也是一开一关挺麻烦的,不能总麻烦人家。秉承着工作是自己的,麻烦到头来总要自己解决的原则,开始找方案。
很简单的图形验证码

第一个是发现可以把验证码图片给AI去解析,得到的答案正确率很高,就是不能调用太多,毕竟token是要花钱的,虽然量不大,但便宜归便宜,付费上班总归不是办法。然后又在网上找到了用OCR的方案,商用OCR当然也是要付费的,好在我们有伟大的开源方案,大不了自己训练嘛,这么简单的验证码,应该还挺好识别的吧...

主角

最后选择的就是开源的Tesseract-OCR,目前的版本是5.4.1,需要自己编译。好在也有懒人解决方案,在用户手册里可以找到第三方提供的编译好的安装包,提供了三个常用的系统,包括Windows。从链接进去就可以找到下载链接,不过Windows安装包提供的版本是5.4.0,并不是最新的...也不差多少是了。

下载安装,完成后会提供十数个命令行工具,其中用来做识别的是tesseract,其它的都不会用XD。
安装包只包含一个eng的训练数据(也就是模型),在安装位置的tessdata文件夹中,其它的模型需要到库中自行下载,就像它自己的介绍,提供了100+种语言。不过验证码是这样的,只需要数字和几个运算符,用英语也足够了,总之先尝试一下吧!

tesseract imageFile outputFile
# outputFile 使用 - 可以直接把结果打印在窗口中
# 使用 -l 选项指定使用的模型,只需要输入名称

很可惜,效果并不好,看来,想用的话只能自己训练了。

开始训练

训练是个很麻烦的事情,尤其是对我这种门外汉来说,在做的时候主要参考了这篇博客和官方用户手册,以及官方提供的一个简化训练步骤的脚本库

1 准备训练图片

验证码的图片直接从登录页面拿就可以了,不过训练的话,即使构成简单还是要用几百张的,最好是用接口去获取图片,又因为训练脚本只支持TIFFPNG类型的图片,其它类型的图片需要在保存时转换格式,还是要写个脚本来处理这个事情的。

import base64
from requests import get

for i in range(1, 101):
    r = get("Url") # 验证码获取链接
    if r.ok:
        j = r.json()
        s = j["data"]["code"] # 解析到图片数据,没有做验证,有问题的话就报错了
        p = base64.b64decode(s) # base64数据需要解码
        with open(f"./trainImage/{i}.png", "wb+") as f: # 创建文件
            f.write(p)
            print(f'write complete.{i}.png')
    else: # 请求出错时调试用的
        print(r.text)
        break

这时,我遇到的第一个问题出现了,倒不是获取图片遇到问题,而是每个训练图片都是需要答案的,答案要保存在和图片同名的后缀为.gt.txt文件中,比如图片文件名是1.png,那么答案文件就是1.gt.txt,文件的内容只包含图片中的文本答案,比如上面那张图,它的答案应该写

6x4=?

不需要换行,但是应该包含图片中的所有目标字符。

注意到这个事情时,我已经保存了一百张图片,不多,但是手动创建文件还是很麻烦的,还要打开图片,还有写入文件。所以,这种事还是交给脚本来做吧XD~
我的思路是写一个UI程序,按顺序加载图片,再准备一个输入框,看到图片后我把答案输进去,再由脚本把答案写到对应文件里。非常的清晰XD。下面是我用的脚本

import logging
import tkinter as tk

class App(tk.Frame):
    def __init__(self, master: tk.Tk) -> None:
        super().__init__(master)
        self.master = master
        self.image = read_image()
        self.create_widget()

    def create_widget(self):
        self.label = tk.Label(self.master, image=self.image)
        self.label.grid(column=0,row=0)

        self.input = tk.Entry(self.master, width=6 ,font=('Arial', 14))
        self.input.bind('<Return>', self.complete)
        self.input.grid(column=0,row=1)

        self.label1 = tk.Label(self.master, text="=?", font=('Arial', 14))
        self.label1.grid(row=1,column=1)

        self.button2 = tk.Button(self.master, text="刷新", command= self.refresh_img)
        self.button2.grid(row=0, column=1)

        self.button = tk.Button(self.master, text="写入", command= self.complete)
        self.button.grid(column=2,row=1)

    def complete(self, event=''):
        content = self.input.get()
        if len(content) == 0 :
            logging.warning('未输入数据')
            return
        save_file(content)
        global I_NUM
        I_NUM += 1
        self.refresh_img()
        self.input.delete(0, tk.END)
    
    def refresh_img(self):
        img = read_image()
        if img:
            self.image = img
            self.label.configure(image= self.image)


def save_file(content):
    file_name = f"{I_NUM}.gt.txt"
    with open(image_dir + file_name, 'w+') as f:
        i = f.write(content + '=?') # 我这个验证码最后两个都是'=?' 所以偷个懒XD
    logging.info(f'成功向文件{file_name}写入{i}个字节') # 会打印在命令行里,UI里没写显示提示信息的功能
    return i

def read_image():
    image_name = f"{I_NUM}.png"
    image = image_dir + image_name
    return tk.PhotoImage(name= image_name, file= image)
# 文件不存在的话会报错,错误信息都会打印在命令行里,UI里没写显示提示信息的功能


I_NUM = 1 # 其实是图片的名称,这样我可以比较简单的控制从哪张图片开始
image_dir = "D:\\tesseract\\trainImage\\" # 包含图片的文件夹,答案文件也保存在这里了

gui = tk.Tk()
gui.geometry('400x250') # 设置了UI大小,不然太小我总是找不到
app = App(gui)
app.mainloop()

这样就可以只输入验证码来创建答案文件了XD

已经保存的图片创建完了,对新获取图片的话,只需要把上面两个脚本合并起来,再稍微修改一下read_image就好了。

# 省略其它代码
def read_image():
    image_name = f"{I_NUM}.png"
    image = image_dir + image_name
    if get_image(image): # 获取失败的时候点【刷新】就能重新获取了
        return tk.PhotoImage(name= image_name, file= image)

def get_image(image):
    r = get("Url")
    if r.ok:
        j = r.json()
        s = j["data"]["code"] # 解析到图片数据,没有做验证,有问题的话就报错了
        p = base64.b64decode(s) # base64数据需要解码
        with open(f"./trainImage/{I_NUM}.png", "wb+") as f: # 创建文件
            f.write(p)
        logging.debug(f'write complete.{I_NUM}.png')
        return True
    else:
        logging.error("获取图片失败")
        return False

把读取本地文件改成先获取再读就好了,缺少的依赖请自行添加。
这样手动准备个两三百张图片就差不多了。

2 尝试使用训练脚本进行训练

把训练脚本的库下载下来,然后按使用说明把环境配置好(Windows):

  • 把Tesseract的目录添加到环境变量
  • 安装Python3和依赖(在requirements.txt里)
  • 需要一些Linux工具,有安装Git的话,直接用Git Bash就行
  • 安装make(文档里是用winget安装的)

训练脚本的说明文档很清晰,如果能认真看的话,我就能少踩俩坑了:(
在训练前,在下载好的脚本文件夹里,先在data文件夹中新建一个MODEL_NAME-ground-truth的文件夹,再把训练用的图片数据复制到这个文件夹里。MODEL_NAME是训练出的模型的名称,只要不重复就行。然后在Git Bash里打开脚本文件夹。
脚本的入口是MakeFile,最简单的训练命令是

make training

但是只下载脚本库是运行不了。脚本里提供了一系列的变量,全部都有默认值。MODEL_NAME就是一个,指模型名称,现在只需要把这个变量填进去就能运行了;D

make training MODEL_NAME=模型名称

虽然运行起来了,但是这么多变量,显然不可能都用默认就能训练出合适的模型(比如我,这样直接训练出来的模型,正确率不到个位数,当然如果训练出来就能用那算你运气好)。这些变量中,NET_SPEC是比较重要的一个,包含了初始的训练参数。而其它变量大多数都是输出位置,关联文件位置等,即使使用默认,也不会影响训练效果。
不过,在调整参数前,还有一件事需要做。

3 图片处理

处理图片消耗了我不少的时间,毕竟这个东西我也要现找方案XD

其实在Tesseract的使用文档中,图片的处理也是放在前面的,就是提高质量。在文档中,它解释了清晰的图片才有准确的识别结果,并提供了一些处理图片的方法和工具。这些方法同样可以用来处理训练图片,而处理的目标,就是尽可能消除噪音、去掉背景、或是让图片更符合识别引擎,在这个过程中我参考了许多教程,比如这篇

图片处理起来很费时,要选择方法,不断调整参数。调试时总是用一张图,然而一个方案在这张图上合适时,对其它图片也可能不尽人意。我做了三四个版本,每个版本的训练效果都没什么明显的提示。大概是我比较菜吧:(

最后我选择的处理方案就是不处理,除了把图片的宽高缩放到了原本的一半。原图片高是48像素,缩小后就是24像素。会这么做的原因是:之前的模型识别后,总是会多一些字符,所以我想,是不是图比较大,所以噪点部分容易被识别,如果把图片缩小一点儿,会不会就不会识别噪点了。
很奇怪的想法,但是实验一下很简单,所以就这样做了。把图片缩放后再去识别,同一张图片,真的就比原图识别结果贴近很多,至少没有了多很多字符。既然如此,把图片都处理好,接下来大概可以开始训练了?

4 调整训练参数

看来还不能开始训练,还有一个重要参数没有调整,就是NET_SPEC

标签:训练,模型,self,Tesserast,验证码,OCR,image,图片
From: https://www.cnblogs.com/soon003/p/18502860

相关文章

  • 京东创作平台旋转验证码识别
     昨天京东创作平台验证码又更新了,变成了这种旋转验证码。经过我们一天的努力,终于完成了这款验证码的数据标记,模型训练。现在正确率达到了几乎100%。识别代码只需要获取图片链接,下载图片得到原图,使用下面代码就可以识别角度,然后根据角度计算滑动距离,就可以自动完成验证imp......
  • 阿里云手机验证码
    阿里云手机验证码在Java中使用阿里云的短信服务发送手机号验证码,你需要使用阿里云提供的SMSSDK。添加依赖<dependency><groupId>com.aliyun.dysmsapi</groupId><artifactId>dysmsapi</artifactId><version>1.0.0</version></dependency>......
  • EasyCaptcha验证码图
    EasyCaptcha在Java中实现EasyCaptcha工具类可以生成验证码,使用图像或简单的数学问题进行验证。官方文档。引入依赖<dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId></dependency>验证码工具的API介绍publicclass......
  • 小红书最新旋转验证码识别代码
    一、简介小红书又出了这种新的旋转验证码,是一种中国国画风格的图片,中间是旋转小图,并且带有随机黑色阴影。这给识别带来了很大难度。而且中间图片内容比较空旷,也给特征提取带来了难度。二、识别介绍经过我们的努力,识别这款新验证码的正确率已经达到了90%以上,大家只需要下载......
  • 设计登陆验证码的逻辑
    今日面试某信息的场景题提到了下这个问题,没太搞懂面试官具体想让我说的就是两个接口的设计,一个生成验证码,一个校验验证码,和其具体的设计方案,没听懂当时的意思,有的小懵逼,因此回头总结回顾一下,日常开发中对这些小细节还是要注重一下,不能因为场景逻辑简单就不去分析其背后的原因。设......
  • 影刀RPA实战:识别简单计算验证码
    1.官方计算验证码基于影刀AI引擎的验证码识别指令,该指令不是长期免费,有一定的免费额度,用完之后需要我们到影刀官方充值。上图使我们要识别的计算验证码影刀指令代码:配置我们选择计算题,文件路径本次指定本地图片,实际中使用网页截图保存图片地址实现演示:计算结果166......
  • 安装LaTeX-OCR截图识别转换成LaTeX-Markdown格式
    安装LaTeX-OCR在虚拟环境中,通过pip命令安装LaTeX-OCR。以下是一个示例命令:pipinstallpix2tex[gui]注意:如果你在中国大陆地区,由于网络原因,可能需要设置临时代理或使用国内的镜像源来加速下载。使用LaTeX-OCR命令行使用安装完成后,你可以在命令行中直接使用LaTeX-OCR。首先,你......
  • 后端java——如何为你的网页设置一个验证码
    本文通过HUTOOL实现:Hutool参考文档Hutool,Java工具集https://hutool.cn/docs/#/1、工具的准备如果我们通过hutool来实现这个功能,我们需要提前安装hutool的jar包。下载地址:CentralRepository:cn/hutool/hutool-all/5.8.16 将下载好的jar包放到eclipse的lib目录,我们proje......
  • DAY75WEB 攻防-验证码安全篇&接口滥用&识别插件&复用绕过&宏命令填入&滑块类
    知识点:1、验证码简单机制-验证码过于简单可爆破2、验证码重复使用-验证码验证机制可绕过3、验证码智能识别-验证码图形码被可识别4、验证码接口调用-验证码触发接口可枚举图片验证码-识别插件-登录爆破&接口枚举验证码识别绕过等技术适用于:口令存在爆破,接口枚举调用,任意......
  • 一招帮你搞定验证码设置
    目录一、准备环境​编辑 二、配置环境三、基础方法四、验证码的一些常用类别1.LineCaptcha线段干扰的验证码(Java)2.CircleCaptcha圆圈干扰的验证码(Java)3.ShearCaptcha扭曲干扰验证码4.写出到浏览器输出5.自定义验证码CodeGenerator 五、通过实例来简述过程1.一个......