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

相关文章

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

C#实现一键批量合并PDF文档

《C#实现一键批量合并PDF文档》这篇文章主要为大家详细介绍了如何使用C#实现一键批量合并PDF文档功能,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言效果展示功能实现1、添加文件2、文件分组(书签)3、定义页码范围4、自定义显示5、定义页面尺寸6、PDF批量合并7、其他方法

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

MyBatis ParameterHandler的具体使用

《MyBatisParameterHandler的具体使用》本文主要介绍了MyBatisParameterHandler的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一、概述二、源码1 关键属性2.setParameters3.TypeHandler1.TypeHa

Spring 中的切面与事务结合使用完整示例

《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录 一、前置知识:Spring AOP 与 事务的关系 事务本质上就是一个“切面”二、核心组件三、完