开源通用验证码识别OCR —— DdddOcr 源码赏析(一)

文章目录

    • @[toc]
  • 前言
  • DdddOcr
  • 环境准备
    • 安装DdddOcr
    • 使用示例
  • 源码分析
    • 实例化DdddOcr
      • 实例化过程
    • 分类识别
      • 分类识别过程
  • 未完待续

前言

DdddOcr 源码赏析
在这里插入图片描述

DdddOcr

DdddOcr是开源的通用验证码识别OCR
官方传送门

环境准备

安装DdddOcr

pip install ddddocr

使用示例

示例图片如下
在这里插入图片描述


import ddddocr

ocr = ddddocr.DdddOcr(show_ad=False)

image = open("example.png", "rb").read()
result = ocr.classification(image)
print(result)
# 识别结果 aFtf

源码分析

我们以实例代码为例,分析源码里面都做了什么

实例化DdddOcr

ocr = ddddocr.DdddOcr(show_ad=False)

对应源码如下

class DdddOcr(object):
    def __init__(self, ocr: bool = True, det: bool = False, old: bool = False, beta: bool = False,
                 use_gpu: bool = False,
                 device_id: int = 0, show_ad=True, import_onnx_path: str = "", charsets_path: str = ""):
        if show_ad:
            print("欢迎使用ddddocr,本项目专注带动行业内卷,个人博客:wenanzhe.com")
            print("训练数据支持来源于:http://146.56.204.113:19199/preview")
            print("爬虫框架feapder可快速一键接入,快速开启爬虫之旅:https://github.com/Boris-code/feapder")
            print(
                "谷歌reCaptcha验证码 / hCaptcha验证码 / funCaptcha验证码商业级识别接口:https://yescaptcha.com/i/NSwk7i")
        if not hasattr(Image, 'ANTIALIAS'):
            setattr(Image, 'ANTIALIAS', Image.LANCZOS)
        self.use_import_onnx = False
        self.__word = False
        self.__resize = []
        self.__charset_range = []
        self.__channel = 1
        if import_onnx_path != "":
            det = False
            ocr = False
            self.__graph_path = import_onnx_path
            with open(charsets_path, 'r', encoding="utf-8") as f:
                info = json.loads(f.read())
            self.__charset = info['charset']
            self.__word = info['word']
            self.__resize = info['image']
            self.__channel = info['channel']
            self.use_import_onnx = True

        if det:
            ocr = False
            self.__graph_path = os.path.join(os.path.dirname(__file__), 'common_det.onnx')
            self.__charset = []

实例化过程

1 show_ad
先来一波广告推广,开源不易,尤其是DdddOcr这么良心的开源Ocr,大家多多支持DdddOcr
2 ANTIALIAS 判断

if not hasattr(Image, 'ANTIALIAS'):
    setattr(Image, 'ANTIALIAS', Image.LANCZOS)

Image.LANCZOS,这是一种图像重采样过滤器,通常用于图像缩放时减少锯齿状边缘和模糊。
这段代码的作用主要是向后兼容或者为旧代码提供一种便捷的访问方式,使得即使PIL或Pillow库的官方API中没有直接提供ANTIALIAS这个属性,开发者也可以通过这种方式来使用LANCZOS过滤器进行图像缩放等操作。

3 然后初始化一些变量

self.use_import_onnx = False
        self.__word = False
        self.__resize = []
        self.__charset_range = []
        self.__channel = 1

4 判断是否使用自己的Ocr模型

if import_onnx_path != "":
     det = False
      ocr = False
      self.__graph_path = import_onnx_path
      with open(charsets_path, 'r', encoding="utf-8") as f:
          info = json.loads(f.read())
      self.__charset = info['charset']
      self.__word = info['word']
      self.__resize = info['image']
      self.__channel = info['channel']
      self.use_import_onnx = True

如果使用自己的Ocr模型,通过import_onnx_path指定模型路径,同时charsets_path指定字符集信息
5 是否启用目标检测

if det:
    ocr = False
    self.__graph_path = os.path.join(os.path.dirname(__file__), 'common_det.onnx')
    self.__charset = []
     

1.6 是否启用ocr
beta为True表示启用新的ocr模型, 为False启用老的ocr模型

if ocr:
    if not beta:
        self.__graph_path = os.path.join(os.path.dirname(__file__), 'common_old.onnx')
        self.__charset = [....]
    else:
        self.__graph_path = os.path.join(os.path.dirname(__file__), 'common.onnx')
        self.__charset = [...]

6 是否启用GPU

 if use_gpu:
    self.__providers = [
          ('CUDAExecutionProvider', {
              'device_id': device_id,
              'arena_extend_strategy': 'kNextPowerOfTwo',
              'cuda_mem_limit': 2 * 1024 * 1024 * 1024,
              'cudnn_conv_algo_search': 'EXHAUSTIVE',
              'do_copy_in_default_stream': True,
          }),
      ]
  else:
      self.__providers = [
          'CPUExecutionProvider',
      ]

这里根据use_gpu来决定是使用GPU还是CPU作为计算提供者(ExecutionProvider)

如果use_gpu为True,即决定使用GPU进行计算,那么会创建一个名为CUDAExecutionProvider的提供者配置列表,并设置了一系列与CUDA(GPU计算平台)相关的参数。这些参数包括:

  1. device_id:指定使用的GPU设备的ID,这允许在多GPU环境中选择特定的GPU进行计算。
  2. arena_extend_strategy:内存分配策略,这里设置为’kNextPowerOfTwo’,意味着内存分配时会向上取到最近的2的幂次方大小,这有助于减少内存碎片。
  3. cuda_mem_limit:限制CUDA设备(GPU)的内存使用量,这里设置为2GB(2 * 1024 * 1024 * 1024字节)。
  4. cudnn_conv_algo_search:指定卷积算法搜索策略,'EXHAUSTIVE’表示使用穷举搜索策略来找到最佳的卷积算法,这可能会增加预处理时间但可能提高执行效率。
  5. do_copy_in_default_stream:指定是否在默认流中执行数据复制操作,这里设置为True。

如果use_gpu为False,即决定使用CPU进行计算,那么会简单地设置计算提供者列表为仅包含一个’CPUExecutionProvider’的列表。

7 加载onnx模型

self.__ort_session = onnxruntime.InferenceSession(self.__graph_path, providers=self.__providers)

❓疑问❓
从代码来看只能加载一种模型,ocr模型(新/旧)、det模型、自己的onnx模型,三种模型三选一,这里self.__graph_path指定模型路径时,却使用了3个if, 而不是if-elif-else结构,个人感觉不太合理, 只能说瑕不掩瑜

源码结构如下

if import_onnx_path != "":
   	self.__graph_path = import_onnx_path
if det:
	self.__graph_path = os.path.join(os.path.dirname(__file__), 'common_det.onnx')
if ocr:
	if not beta:
		self.__graph_path = os.path.join(os.path.dirname(__file__), 'common_old.onnx')
	else:
        self.__graph_path = os.path.join(os.path.dirname(__file__), 'common.onnx')

分类识别

image = open("example.jpg", "rb").read()
result = ocr.classification(image)
print(result)

对应源码如下

def classification(self, img, png_fix: bool = False, probability=False):
        if self.det:
            raise TypeError("当前识别类型为目标检测")
        if not isinstance(img, (bytes, str, pathlib.PurePath, Image.Image)):
            raise TypeError("未知图片类型")
        if isinstance(img, bytes):
            image = Image.open(io.BytesIO(img))
        elif isinstance(img, Image.Image):
            image = img.copy()
        elif isinstance(img, str):
            image = base64_to_image(img)
        else:
            assert isinstance(img, pathlib.PurePath)
            image = Image.open(img)
        if not self.use_import_onnx:
            image = image.resize((int(image.size[0] * (64 / image.size[1])), 64), Image.ANTIALIAS).convert('L')
        else:
            if self.__resize[0] == -1:
                if self.__word:
                    image = image.resize((self.__resize[1], self.__resize[1]), Image.ANTIALIAS)
                else:
                    image = image.resize((int(image.size[0] * (self.__resize[1] / image.size[1])), self.__resize[1]),
                                         Image.ANTIALIAS)
            else:
                image = image.resize((self.__resize[0], self.__resize[1]), Image.ANTIALIAS)
            if self.__channel == 1:
                image = image.convert('L')
            else:
                if png_fix:
                    image = png_rgba_black_preprocess(image)
                else:
                    image = image.convert('RGB')
        image = np.array(image).astype(np.float32)
        image = np.expand_dims(image, axis=0) / 255.
        if not self.use_import_onnx:
            image = (image - 0.5) / 0.5
        else:
            if self.__channel == 1:
                image = (image - 0.456) / 0.224
            else:
                image = (image - np.array([0.485, 0.456, 0.406])) / np.array([0.229, 0.224, 0.225])
                image = image[0]
                image = image.transpose((2, 0, 1))

        ort_inputs = {'input1': np.array([image]).astype(np.float32)}
        ort_outs = self.__ort_session.run(None, ort_inputs)
        result = []

        last_item = 0

        if self.__word:
            for item in ort_outs[1]:
                result.append(self.__charset[item])
        else:
            if not self.use_import_onnx:
                # 概率输出仅限于使用官方模型
                if probability:
                    ort_outs = ort_outs[0]
                    ort_outs = np.exp(ort_outs) / np.sum(np.exp(ort_outs))
                    ort_outs_sum = np.sum(ort_outs, axis=2)
                    ort_outs_probability = np.empty_like(ort_outs)
                    for i in range(ort_outs.shape[0]):
                        ort_outs_probability[i] = ort_outs[i] / ort_outs_sum[i]
                    ort_outs_probability = np.squeeze(ort_outs_probability).tolist()
                    result = {}
                    if len(self.__charset_range) == 0:
                        # 返回全部
                        result['charsets'] = self.__charset
                        result['probability'] = ort_outs_probability
                    else:
                        result['charsets'] = self.__charset_range
                        probability_result_index = []
                        for item in self.__charset_range:
                            if item in self.__charset:
                                probability_result_index.append(self.__charset.index(item))
                            else:
                                # 未知字符
                                probability_result_index.append(-1)
                        probability_result = []
                        for item in ort_outs_probability:
                            probability_result.append([item[i] if i != -1 else -1 for i in probability_result_index ])
                        result['probability'] = probability_result
                    return result
                else:
                    last_item = 0
                    argmax_result = np.squeeze(np.argmax(ort_outs[0], axis=2))
                    for item in argmax_result:
                        if item == last_item:
                            continue
                        else:
                            last_item = item
                        if item != 0:
                            result.append(self.__charset[item])
                    return ''.join(result)

            else:
                last_item = 0
                for item in ort_outs[0][0]:
                    if item == last_item:
                        continue
                    else:
                        last_item = item
                    if item != 0:
                        result.append(self.__charset[item])
                return ''.join(result)

分类识别过程

1 目标检测任务不支持分类

if self.det:
	raise TypeError("当前识别类型为目标检测")

  1. 图片格式转换
 if not isinstance(img, (bytes, str, pathlib.PurePath, Image.Image)):
            raise TypeError("未知图片类型")
        if isinstance(img, bytes):
            image = Image.open(io.BytesIO(img))
        elif isinstance(img, Image.Image):
            image = img.copy()
        elif isinstance(img, str):
            image = base64_to_image(img)
        else:
            assert isinstance(img, pathlib.PurePath)
            image = Image.open(img)

未完待续

明天见

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

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

相关文章

Wyn商业智能助力零售行业数字化决策高效驱动

最新技术资源(建议收藏) https://www.grapecity.com.cn/resources/ 项目背景及痛点 百利商业的业务覆盖赛格、 SKP、奥莱、王府井等多地区具有代表性的商场,并创立了多个自有品牌。随着新零售模式的兴起,百利商业紧跟时代步伐&am…

培训学校课程管理系统-计算机毕设Java|springboot实战项目

🍊作者:计算机毕设匠心工作室 🍊简介:毕业后就一直专业从事计算机软件程序开发,至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长:按照需求定制化开发项目…

web开发,过滤器,前后端交互

目录 web开发概述 web开发环境搭建 Servlet概述 Servlet的作用: Servlet创建和使用 Servlet生命周期 http请求 过滤器 过滤器的使用场景: 通过Filter接口来实现: 前后端项目之间的交互: 1、同步请求 2、异步请求 优化…

Mysql(三)---增删查改(基础)

文章目录 前言1.补充1.修改表名1.2.修改列名1.3.修改列类型1.4.增加新列1.5.删除指定列 2.CRUD3.新增(Create)3.1.单行插入3.2.指定列插入3.3.多行插入 4.数据库的约束4.1.约束的分类4.2.NULL约束4.3.Unique约束4.4.Default 默认值约束4.5.PRIMARY KEY:主键约束4.6.…

Facebook与区块链:社交网络如何融入去中心化技术

随着区块链技术的飞速发展,去中心化理念逐渐渗透到各个领域,社交网络也不例外。作为全球领先的社交平台,Facebook在这一趋势下开始积极探索区块链技术的潜力,希望利用这一前沿技术来提升平台的安全性、透明度和用户控制权。本文将…

景联文科技:一文详解如何构建高质量SFT数据

在图像处理和计算机视觉领域中,将一张图像转化为可用于训练机器学习模型的数据是一项复杂而重要的任务。SFT(Supervised Fine-Tuning,监督微调)是一种常见的深度学习策略,在这一过程中发挥着核心作用。 SFT是指在一个预…

alibabacloud学习笔记14

阿里云ECS服务器Docker部署Nacos docker拉取镜像 查看容器: 启动docker: 查看启动日志: 阿里云ECSDocker部署Sentinel实战 docker拉取镜像 启动镜像: 查看日志: 记得网络安全组开放端口。 阿里云ECSDocker部署Zipkin实…

tekton什么情况下在Dockerfile中需要用copy

kaniko配置如下 如果docker中的workDir跟tekton中的workDir不一致需要copy。也可以通过mv,cp达到类似效果

vue前端可以完整的显示编辑子级部门,用户管理可以为用户分配角色和部门?

用户和角色是一对多的关系用户和部门是多对多得关系<template><div class="s"><!-- 操作按钮 --><div class="shang"><el-input v-model="searchText" placeholder="请输入搜索关键词" style="width:…

效果炫酷的3D翻转书特效WordPress主题模板MagicBook主题v1.19

正文&#xff1a; MagicBook是一款支持3D翻书特效的书籍WordPress主题。支持可视化页面搭建&#xff0c;3D菜单&#xff0c;完全自适应设计,WPML多语言支持。 这款主题一定会让你爱不释手。虽然他是英文的&#xff0c;但不可不承认的是&#xff0c;它优雅的设计会让你愿意花时…

如何在桌面同时展示多个窗口

一、实现2分屏显示 win箭头 二、实现3分屏显示 1. 在实现2分屏显示的基础上&#xff0c;再次点击箭头图标&#xff0c;这次选择屏幕的上方或下方。 2. 点击后&#xff0c;第三个窗口将会出现在你选择的区域。现在&#xff0c;你可以在三个窗口之间自由切换&#xff0c;提高工…

数值计算引擎:搭建远程容器开发环境

Build VS Code Remote Docker Development Environment 大型CAE软件开发技术栈通常依赖多个第三方库&#xff0c;因此从零开始配置开发、编译、运行等环境通常较为繁琐。但随着公司的发展壮大&#xff0c;却经常需要为新加入的成员配备相应的开发环境&#xff1b;另外&#xf…

手把手教你安装Jupyter Notebook(保姆级教程)

Jupyter Notebook介绍 什么是Jupyter Notebook Jupyter Notebook 是一个基于 Web 的交互式计算环境&#xff0c;支持多种编程语言&#xff0c;包括 Python、R、Julia 等。它的主要功能是将代码、文本、数学方程式、可视化和其他相关元素组合在一起&#xff0c;创建一个动态文…

数字化转型底座-盘古信息IMS OS,可支撑构建MES/WMS/QCS/IoT等工业软件

在当今这个数字化浪潮汹涌的时代&#xff0c;众多企业纷纷踏上数字化转型之路。对于部分想自研工业软件的企业来说&#xff0c;一个强大、灵活且可扩展的数字化底座显得尤为重要。盘古信息IMS OS&#xff0c;&#xff0c;正是这样一款能够支撑构建MES&#xff08;制造执行系统&…

ubuntu server 扩容

环境&#xff1a;VirtualBox、Ubuntu-server 调整虚拟磁盘大小 在 VirtualBox 主界面 工具 -- 介质 中选择你要操作的虚拟磁盘&#xff0c;点击属性&#xff0c;更改大小即可&#xff0c;保存后启动虚拟机 查看磁盘状态 lsblk 可以看到 sda 已经是 128G 了。ubuntu--vg-ubun…

【前端】VUE动态引入组件 通过字符串动态渲染模板 动态生成组件

【前端】VUE动态引入组件 通过字符串动态渲染模板 应用场景&#xff1a; js增强 动态代码 扩展一类的 可以配合低代码平台等 灵活配置在线表单 在线列表等 适合灵活性 扩展性比较强的组件 VUE2 <template><div><textarea v-model"templateString"…

开源版最新LoveCardsV2表白墙源码下载

源码亮点 模板系统&#xff0c;给你无限可能 卡片不限字数&#xff0c;支持多图片上传 支持评论&#xff0c;点赞&#xff0c;让互动性拉满 管理后台可添加多个管理员 卡片一键分享至多平台 卡片浏览次数统计 发行版开箱即用 部署教程 1. 环境&#xff08;参考开发环境&…

git-版本管理工具基本操作-创建仓库-拉取-推送-暂存库-版本库

1、创建仓库 2、克隆仓库到本地&#xff08;首次拉取需要输入用户名和密码&#xff0c;用户名用邮箱&#xff0c;密码用登录gitee的密码&#xff0c;后面配置密钥后可以直接clone&#xff09; 在命令行输出两行指令配置git才能克隆&#xff1a; username&#xff1a;gitee账号…

快速排序(Java实现)

目录 快速排序的思想 代码实现 思路 代码 快速排序的特点 快速排序的思想 快速排序和冒泡排序一样&#xff0c;是一种交换排序。快速排序的核心思想也是分治&#xff0c;分而治之。给定一个数组&#xff0c;先选定一个元素作为枢轴&#xff0c;然后将大于枢轴的放在右边&a…

成为Python砖家(3): 何时产生字节码 .pyc 文件

好奇&#xff1a;.pyc和 __pycache__是啥&#xff1f; 你是否好奇&#xff0c;在某些 Python 工程中&#xff0c;当执行了 xxx.py脚本后&#xff0c;多出了 __pycache__目录&#xff1f;这个目录下存放的是一些 .pyc结尾的文件。 这些文件&#xff0c;叫做 python bytecode。 …