实战:使用 OpenCV 和 PyTesseract 对文档进行 OCR

2023-12-24 06:28

本文主要是介绍实战:使用 OpenCV 和 PyTesseract 对文档进行 OCR,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

随着世界各地的组织都希望将其运营数字化,将物理文档转换为数字格式是非常常见的。这通常通过光学字符识别 (OCR) 完成,其中文本图像(扫描的物理文档)通过几种成熟的文本识别算法之一转换为机器文本。当在干净的背景下处理打印文本时,文档 OCR 的性能最佳,具有一致的段落和字体大小。

在实践中,这种情况远非常态。发票、表格甚至身份证明文件的信息分散在整个文件空间中,这使得以数字方式提取相关数据的任务变得更加复杂。

在本文中,我们将探索一种使用 Python 为 OCR 定义文档图像区域的简单方法。我们将使用信息分散在整个文档空间的文档示例 —— 护照。以下样本护照放置在白色背景中,模拟复印的护照副本。

从此护照图像中,我们希望获得以下字段:

名字/名字姓氏中文名汉字的姓氏护照号码

首先,我们将导入所有必需的包。最重要的包是用于计算机视觉操作的 OpenCV 和 PyTesseract,它是强大的 Tesseract OCR 引擎的 Python 包装器。

from cv2 import cv2import pytesseractimport pandas as pdimport numpy as npimport mathfrom matplotlib import pyplot as plt

接下来,我们将使用 cv2.imread 读取我们的护照图像。我们的第一个任务是从这个伪扫描页面中提取实际的护照文件区域。我们将通过检测护照的边缘并将其从图像中裁剪出来来实现这一点。

img = cv2.imread('images\Passport.png',0)img_copy = img.copy()img_canny = cv2.Canny(img_copy, 50, 100, apertureSize = 3)

OpenCV 库中包含的 Canny 算法使用多阶段过程来检测图像中的边缘。使用的最后三个参数是较低阈值和较高阈值(分别为 minVal 和 maxVal),以及内核大小。

运行 Canny 算法会产生以下输出。请注意,由于选择了低阈值,因此保留了最少的边缘。

img_hough = cv2.HoughLinesP(img_canny, 1, math.pi / 180, 100, minLineLength = 100, maxLineGap = 10)

接下来,我们在边缘检测图像上使用另一种称为霍夫变换的算法,通过检测线绘制出护照区域的形状。minLineLength 参数定义了一个形状必须包含多少像素才能被视为 “线”,而 maxLineGap 参数表示像素序列中被视为相同形状的最大允许间隙。

(x, y, w, h) = (np.amin(img_hough, axis = 0)[0,0], np.amin(img_hough, axis = 0)[0,1], np.amax(img_hough, axis = 0)[0,0] - np.amin(img_hough, axis = 0)[0,0], np.amax(img_hough, axis = 0)[0,1] - np.amin(img_hough, axis = 0)[0,1])img_roi = img_copy[y:y+h,x:x+w]

我们的护照四面都是直线 —— 文件的边缘。因此,有了我们的线条信息,我们可以选择通过检测到的线条的外边缘来裁剪我们的护照区域:

将护照竖直旋转后,我们开始在图像中选择要捕获数据的区域。几乎所有国际护照都符合 ICAO 标准,该标准概述了护照页的设计和布局规范。这些规范之一是机读区 (MRZ),即护照文件底部有趣的两行。你们的文件的视觉检查区 (VIZ) 中的大部分关键信息也包含在机读区中,机器可以读取这些信息。在我们的练习中,那台机器是我们值得信赖的 Tesseract 引擎。

img_roi = cv2.rotate(img_roi, cv2.ROTATE_90_COUNTERCLOCKWISE)(height, width) = img_roi.shapeimg_roi_copy = img_roi.copy()dim_mrz = (x, y, w, h) = (1, round(height*0.9), width-3, round(height-(height*0.9))-2)img_roi_copy = cv2.rectangle(img_roi_copy, (x, y), (x + w ,y + h),(0,0,0),2)

让我们使用四个维度定义护照图像中的 MRZ 区域:水平偏移(从左侧)、垂直偏移(从顶部)、宽度和高度。对于 MRZ,我们将假设它包含在我们护照的底部 10% 内。因此,使用 OpenCV 的矩形函数,我们可以在区域周围绘制一个框来验证我们的尺寸选择。

img_mrz = img_roi[y:y+h, x:x+w]img_mrz =cv2.GaussianBlur(img_mrz, (3,3), 0)ret, img_mrz = cv2.threshold(img_mrz,127,255,cv2.THRESH_TOZERO)

在新图像中裁剪所选区域。我们将对裁剪后的图像进行一些基本的图像预处理,以促进更好的读出 —— 高斯模糊和简单阈值。

mrz = pytesseract.image_to_string(img_mrz, config = '--psm 12')

我们现在准备应用 OCR 处理。在我们的 image_to_string 属性中,我们配置了 “带有方向和脚本检测(OSD)的稀疏文本” 的页面分割方法。这旨在捕获我们图像中的所有可用文本。

将 Pytesseract 输出与我们的原始护照图像进行比较,我们可以观察到读取特殊字符时的一些错误。为了获得更准确的读数,可以使用 Pytesseract 的白名单配置进行优化;然而就我们的目的而言,电流读数的准确性就足够了。

mrz = [line for line in mrz.split('\n') if len(line)>10]if mrz[0][0:2] == 'P<':  lastname = mrz[0].split('<')[1][3:]else:  lastname = mrz[0].split('<')[0][5:]firstname = [i for i in mrz[0].split('<') if (i).isspace() == 0 and len(i) > 0][1]pp_no = mrz[1][:9]

根据 ICAO 关于 MRZ 代码结构的指导原则应用一些字符串操作,我们可以提取护照持有人的姓氏、名字和护照号码:

不是英文的文本怎么办?没问题 ——Tesseract 引擎已经为 100 多种语言训练了模型(尽管每种支持的语言的 OCR 性能的稳健性不同)。

img_roi_copy = img_roi.copy()dim_lastname_chi = (x, y, w, h) = (455, 1210, 120, 70)img_lastname_chi = img_roi[y:y+h, x:x+w]img_lastname_chi = cv2.GaussianBlur(img_lastname_chi, (3,3), 0)ret, img_lastname_chi = cv2.threshold(img_lastname_chi,127,255,cv2.THRESH_TOZERO)dim_firstname_chi = (x, y, w, h) = (455, 1300, 120, 70)img_firstname_chi = img_roi[y:y+h, x:x+w]img_firstname_chi = cv2.GaussianBlur(img_firstname_chi, (3,3), 0)ret, img_firstname_chi = cv2.threshold(img_firstname_chi,127,255,cv2.THRESH_TOZERO)

使用相同的区域选择方法,我们再次为目标数据字段定义维度(x、y、w、h),并对裁剪后的图像提取应用模糊和阈值处理。

lastname_chi = pytesseract.image_to_string(img_lastname_chi, lang = 'chi_sim', config = '--psm 7')firstname_chi = pytesseract.image_to_string(img_firstname_chi, lang = 'chi_sim', config = '--psm 7')

现在,在我们的 image_to_string 参数中,我们将添加输入文本的语言脚本,简体中文。

要完成练习,请将所有收集的字段传递给字典并输出到表格以供实际使用。

OCR 感兴趣区域的显式定义只是在 OCR 中获取所需数据的众多方法之一。根据你们的用例,使用其他方法(例如轮廓分析或对象检测)可能最有效,正如我们的护照练习所示,在应用 OCR 之前对图像进行适当的预处理是关键。在处理具有不同图像质量的真实文档时,尝试不同的预处理技术以找到最适合你们的文档类型的方法非常重要。

这篇关于实战:使用 OpenCV 和 PyTesseract 对文档进行 OCR的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/530833

相关文章

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

linux解压缩 xxx.jar文件进行内部操作过程

《linux解压缩xxx.jar文件进行内部操作过程》:本文主要介绍linux解压缩xxx.jar文件进行内部操作,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、解压文件二、压缩文件总结一、解压文件1、把 xxx.jar 文件放在服务器上,并进入当前目录#

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

Python办公自动化实战之打造智能邮件发送工具

《Python办公自动化实战之打造智能邮件发送工具》在数字化办公场景中,邮件自动化是提升工作效率的关键技能,本文将演示如何使用Python的smtplib和email库构建一个支持图文混排,多附件,多... 目录前言一、基础配置:搭建邮件发送框架1.1 邮箱服务准备1.2 核心库导入1.3 基础发送函数二、

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

PowerShell中15个提升运维效率关键命令实战指南

《PowerShell中15个提升运维效率关键命令实战指南》作为网络安全专业人员的必备技能,PowerShell在系统管理、日志分析、威胁检测和自动化响应方面展现出强大能力,下面我们就来看看15个提升... 目录一、PowerShell在网络安全中的战略价值二、网络安全关键场景命令实战1. 系统安全基线核查

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

Ubuntu如何分配​​未使用的空间

《Ubuntu如何分配​​未使用的空间》Ubuntu磁盘空间不足,实际未分配空间8.2G因LVM卷组名称格式差异(双破折号误写)导致无法扩展,确认正确卷组名后,使用lvextend和resize2fs... 目录1:原因2:操作3:报错5:解决问题:确认卷组名称​6:再次操作7:验证扩展是否成功8:问题已解