实战:使用 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

相关文章

C#监听txt文档获取新数据方式

《C#监听txt文档获取新数据方式》文章介绍通过监听txt文件获取最新数据,并实现开机自启动、禁用窗口关闭按钮、阻止Ctrl+C中断及防止程序退出等功能,代码整合于主函数中,供参考学习... 目录前言一、监听txt文档增加数据二、其他功能1. 设置开机自启动2. 禁止控制台窗口关闭按钮3. 阻止Ctrl +

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

mybatis中resultMap的association及collectio的使用详解

《mybatis中resultMap的association及collectio的使用详解》MyBatis的resultMap定义数据库结果到Java对象的映射规则,包含id、type等属性,子元素需... 目录1.reusltmap的说明2.association的使用3.collection的使用4.总

Spring Boot配置和使用两个数据源的实现步骤

《SpringBoot配置和使用两个数据源的实现步骤》本文详解SpringBoot配置双数据源方法,包含配置文件设置、Bean创建、事务管理器配置及@Qualifier注解使用,强调主数据源标记、代... 目录Spring Boot配置和使用两个数据源技术背景实现步骤1. 配置数据源信息2. 创建数据源Be

Java中使用 @Builder 注解的简单示例

《Java中使用@Builder注解的简单示例》@Builder简化构建但存在复杂性,需配合其他注解,导致可变性、抽象类型处理难题,链式编程非最佳实践,适合长期对象,避免与@Data混用,改用@G... 目录一、案例二、不足之处大多数同学使用 @Builder 无非就是为了链式编程,然而 @Builder

在IntelliJ IDEA中高效运行与调试Spring Boot项目的实战步骤

《在IntelliJIDEA中高效运行与调试SpringBoot项目的实战步骤》本章详解SpringBoot项目导入IntelliJIDEA的流程,教授运行与调试技巧,包括断点设置与变量查看,奠定... 目录引言:为良驹配上好鞍一、为何选择IntelliJ IDEA?二、实战:导入并运行你的第一个项目步骤1

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

mybatis-plus QueryWrapper中or,and的使用及说明

《mybatis-plusQueryWrapper中or,and的使用及说明》使用MyBatisPlusQueryWrapper时,因同时添加角色权限固定条件和多字段模糊查询导致数据异常展示,排查发... 目录QueryWrapper中or,and使用列表中还要同时模糊查询多个字段经过排查这就导致只要whe

Spring Boot3.0新特性全面解析与应用实战

《SpringBoot3.0新特性全面解析与应用实战》SpringBoot3.0作为Spring生态系统的一个重要里程碑,带来了众多令人兴奋的新特性和改进,本文将深入解析SpringBoot3.0的... 目录核心变化概览Java版本要求提升迁移至Jakarta EE重要新特性详解1. Native Ima

Spring Boot 与微服务入门实战详细总结

《SpringBoot与微服务入门实战详细总结》本文讲解SpringBoot框架的核心特性如快速构建、自动配置、零XML与微服务架构的定义、演进及优缺点,涵盖开发环境准备和HelloWorld实战... 目录一、Spring Boot 核心概述二、微服务架构详解1. 微服务的定义与演进2. 微服务的优缺点三