
Python使用EasyOCR做一个对多图的连续扫描识别的OCR工具
工作中,我们经常需要对一些图片进行扫描识别,用于将图片中的文字转化为txt文本,再进行编辑处理。而通过Python简单的开发,就能实现OCR(即光学图像识别)的功能。
工作中,我们经常需要对一些图片进行扫描识别,用于将图片中的文字转化为txt文本,再进行编辑处理。而通过Python简单的开发,就能实现OCR(即光学图像识别)的功能。
一 、python中常用的OCR组件
python常用的OCR组件比较多,我测试了四个,包括pytesseract、PaddleOCR、muggle_OCR和EasyOCR。从识别速度上来看,Pytesseract和muggle_OCR速度最快,但在中文的识别率上比较差,PaddleOCR的识别速度和识别率都适中,而EasyOCR的速度最慢,但识别率也是最好的。我最看中的就是识别率,所以我选用了EasyOCR。
二 、工具实现的功能
1 、指定需要识别的图片所在的目录。
2 、实现指定目录图片的预览
3 、以便用户清除无用图片
4 、通过拖动,实现图片文件顺序的调整
5 、对指定图片按顺序进行识别,识别后的文本放入到文本框中,方便用户进行复制粘贴。
三 、应用界面及操作说明
应用界面采用pycharm+pyqt5开发,布局为常用的盒式布局结构:整个窗口使用一个垂直布局,下面包括四个水平布局。界面结构如下:
界面使用box布局,一个垂直QVBox布局,包含个多行水平QHBox布局:
第1行:一个显示原始图片文件所在位置的文本框,一个打开文件夹选择对话框按钮。
第2行:左侧为一个显示指定文件夹内图片文件名的列表框,下面为一个删除按钮,右侧为一个图片,用于预览当前选中的图片文件
第3行:一个文本框,用于显示识别后的文本,可用于复制删除到其它地方。
第4行:一个立即提取的按钮。
如下图所示
主要操作过程:
1、点击第一行的…按钮后,弹出文件夹选择对话框,在选择文件夹后,从文件夹中读取所有PNG、JPG、BMP图像文件,并把文件名加载到第二行左侧的列表中。
2、通过点击选中列表中的文件名,可以在第二行右侧预览出该文件对应的图像。
3、通过拖动列表中的文件名,可以改变图片的处理顺序,也可以点击列表下方的称除按钮从列表中移除该图片(但不删除文件)
5、点击立即提取,完成后即可把识别后的文本显示在第三行的文本框中,文本内容可以通过复制粘贴到word或其它应用中进行处理。
四 、实现代码
代码如下,因为注释比较清除,就不再另行说明了。
'''
一个将多个图片通过easyOCR从多个图片中连续提取文字的应用
#################################################################
主要功能:
1、用于将指定文件夹中的所有图片文件中的文字全部提取并生成一个txt文件。
2、支持jpg、png和bmp图片格式,自动过滤掉其它格式
3、实现图片预览
4、通过拖动,实现图片文件顺序的调整
5、生成后的文本可以复制到其它地方使用
#################################################################
界面介绍:
界面使用box布局,一个垂直QVBox布局,包含个多行水平QHBox布局:
第1行:一个显示原始图片文件所在位置的文本框,一个打开文件夹选择对话框按钮
第2行:左侧为一个显示指定文件夹内图片文件名的列表框,下面为一个删除按钮,右侧为一个图片,用于预览当前选中的图片文件
第3行:一个文本框,用于显示处理进度和处理信息。
第4行:一个居中的开始提取按钮
##################################################################
'''
import os
import sys
from PyQt5.QtCore import pyqtSignal, QThread
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import (QWidget, QPushButton,
QHBoxLayout, QVBoxLayout, QApplication, QLabel,
QLineEdit, QTextEdit, QFileDialog,
QListWidget, QListWidgetItem, QAbstractItemView,QStatusBar
)
import easyocr
class Thread_do(QThread): # 定义线程类
# 定义带参数一个信号及所需参数
_signal =pyqtSignal(str)
#定义几个成员,用于传递参数,包括:源文件夹,图像列表
_picpath=""
_piclist=[]
#重写类线程类的初步化方法,用于将传入的几个参数保存到类成员中
def __init__(self,_path,_piclist):
super().__init__()
self._picpath=_path
self._piclist=_piclist
def run(self):#线程的执行方法
list_images=[]#定义一个列表,用于保存每个图像对象
# 创新easyorc的类,用到两个参数
# 第一个是识别的文本语言类型,我们只需要简体中文和英文,即ch_sim和en
# 第二是是是否使用GPU,据说使用时会加快识别速度,但我试了下,没有什么效果,就用了false,不使用
reader = easyocr.Reader(['ch_sim', 'en'], gpu=False)
for i in range(len(self._piclist)):
fullPicname=self._picpath+"/"+self._piclist[i]#取得路片的全路径,如d:\aaa.png
# 返回一个文本参数,即共需要识别几页,正在处理第页页,文件名是什么,如【开始处理1-3张图片,d:\\aaa.png】
self._signal.emit(f'【开始处理{i + 1}-{len(self._piclist)}张图片,{fullPicname}】\n')
result = reader.readtext(fullPicname)
restxt=""#restxt用于保存识别的内容
for ln in result:
# 因为easyocr得到的文本比较多,如只有第1列的内容是我们需要识别文本,
# 其它的我们用不上,所需就只取第行的第1列,添加到restxt中。
restxt += ln[1]
restxt+="\n" #每页识完成后在restxt中加一个空行。
self._signal.emit(restxt)#返回本页识别的内容
self._signal.emit("全部完成")##返回全部完成信号
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
#定义布局
vbox = QVBoxLayout()
hbox_line1 = QHBoxLayout()
hbox_line2 = QHBoxLayout()
hbox_line3 = QHBoxLayout()
hbox_line4 = QHBoxLayout()
hbox_line5 = QHBoxLayout()
#第一行,一个标签,一个文件框,一个打开对话框按钮
######################################################
self.txt_SrcPath = QLineEdit("D:/") # 原始文件所在位置,默认为C:/
self.txt_SrcPath.setReadOnly(True)#设置为不可编辑
btn_openDiagSrcPath = QPushButton("...") # 选择源文件夹按钮
#绑定按钮的打开事件
btn_openDiagSrcPath.clicked.connect(self.showDialog_Src)
hbox_line1.addWidget(QLabel("图片路径:"))
hbox_line1.addWidget(self.txt_SrcPath)
hbox_line1.addWidget(btn_openDiagSrcPath)
#第二行,一个垂直布局(上面为一个图片文件列表,下面为删除按钮)一个用于显示当前选中的图片
vbox_s1=QVBoxLayout()
self.pic_listbox = QListWidget(self)
self.pic_listbox.setMaximumHeight(300)
self.pic_listbox.setMinimumHeight(300)
# 使列表框中的元素可拖动并设置拖动模式
self.pic_listbox.setAcceptDrops(True)
self.pic_listbox.setDragDropMode(QAbstractItemView.InternalMove)
#绑定列表框中列表项点击的事件(更新预览图片的显示内容)
self.pic_listbox.itemClicked.connect(self.listItemClick)
btn_del=QPushButton("移除选中的文件")
#绑定删除按钮的事件
btn_del.clicked.connect(self.deleteListItem)
vbox_s1.addWidget(self.pic_listbox)
vbox_s1.addWidget(btn_del)
hbox_line2.addLayout(vbox_s1)
# 添加一个图片,用于显示选中的图片
self.lbl_Image = QLabel(self)
#固定预览图片的大小,可以进一步细化为根据宽度自动适应图片比例
self.lbl_Image.setMinimumSize(300,400)
self.lbl_Image.setMaximumSize(300,400)
self.lbl_Image.setText("暂未选择文件")
self.lbl_Image.setScaledContents(True)
hbox_line2.addWidget(self.lbl_Image)
#第3行,处理信息文本框
self.txt_PressInof = QTextEdit() # 处理信息文本框
hbox_line3.addWidget(self.txt_PressInof)
#第4行,立即提取按钮
self.btn_do = QPushButton("立即提取") # 提取按钮
hbox_line4.addWidget(self.btn_do)
#绑定生成按钮的事件
self.btn_do.clicked.connect(self.process_Pic)
#把几个水平布局加入到垂直布局器中
vbox.addLayout(hbox_line1)
vbox.addLayout(hbox_line2)
vbox.addLayout(hbox_line3)
vbox.addLayout(hbox_line4)
self.setLayout(vbox)
#设置窗口大小(1024*840)及初始化位置(100,100)
self.setGeometry(100, 100, 1024, 720)
self.setWindowTitle('Python OCR')#设置标题
self.show()
def showDialog_Src(self): # 选择源文件夹对话框,把选中的文件夹路径保存在txt_srcpath中
folder_path = QFileDialog.getExistingDirectory(self, "选择原始图片文件夹")
self.txt_SrcPath.setText(folder_path)
self.loadPic(folder_path)
itemCount=self.pic_listbox.count()
def listItemClick(self):#列表框中列表项的点击事件,在预览图片中显示该文件
sel_item=self.pic_listbox.selectedItems()
sel_file=os.path.join(self.txt_SrcPath.text(), sel_item[0].text())
pixmap = QPixmap(sel_file) # 按指定路径找到图片
self.lbl_Image.setPixmap(pixmap) # 在label上显示图片
self.lbl_Image.setScaledContents(True) # 让图片自适应label大小
def deleteListItem(self):#删除列表中某一项的事件
current_row = self.pic_listbox.currentRow()
if current_row != -1:
item = self.pic_listbox.takeItem(current_row)
del item
self.pic_listbox.setCurrentRow(-1)
self.lbl_Image.clear()
self.lbl_Image.setText("暂未选择文件")
def loadPic(self,_path):#把指定文件夹中的图片加载到列表框中的事件,在showDialog_Src函数中调用
self.pic_listbox.clear()
for f in os.listdir(_path):
#如果扩展名为jpg,bmp或PNG,就添加到列表框中,注意区分大小写
if f.endswith(".jpg") or f.endswith(".png") or f.endswith(".bmp") \
or f.endswith(".JPG") or f.endswith(".PNG") or f.endswith(".BMP"):
self.pic_listbox.addItem(QListWidgetItem(f))
def process_Pic(self): # 处理按钮的点击事件
# 取得参数,需要几个参数:
# 1、图片所在路径
# 2、需处理图片文件名列表
path=self.txt_SrcPath.text()#图片路径文件夹名
list_pic=[]#定义一个列表,用于保存需处理图片的路径
for i in range(self.pic_listbox.count()):
file_name=self.pic_listbox.item(i).text()
list_pic.append(file_name)
# 生成过程中,改变按钮的状态为不可用,且按钮文本变为正在生成
self.btn_do.setEnabled(False)
self.btn_do.setText("正在提取...")
# 定义线程实例并将参数传入
self.thread_do = Thread_do(path,list_pic)
# 信号连接,如果收到信号,就执行对应的函数
self.thread_do._signal.connect(self.update_TxtInfo)
# 启动线程实例/
self.thread_do.start()
def update_TxtInfo(self, _info): # 定义收到线程返回信号的方法
if _info == "全部完成": # 如收到全部完成,恢复按钮的状态
self.btn_do.setEnabled(True)
self.btn_do.setText("开始提取")
else: # 否则更新文本框的内容
self.txt_PressInof.append(_info)
#################################################################
# 主程序
###############################################################
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
五 、运行测试
测试从两个方面进行,一是对文字内容比较多的图片进行识别,二是通过改变字体,对比较不常见的字体进行分析,如行楷、隶书、舒体,魏碑等。
先进行一个方面,我们从网上截了几个租房合同的模板,共11张,每张的文本的内容比较多,如下:
11个图片识别用了大约不到100秒,每张识别大约需要8秒钟左右,而识别率还是比较高的,除了别的标点符号外,基本上都能识别。
对其它字体的识别,我用一首宋词,设置为多种字体,包括仿宋(主要用于对比)、方正硬笔楷书、方正行楷、方正舒体和方正雅艺的简体和繁体。
图片如下:
下面是仿宋、方正硬笔楷书和方正行楷
下面是方正硬笔行楷和方正舒体
下面是方正简体雅艺和繁体雅艺
我们再次运行测试:
从结果中可以看出:仿宋、楷体easyOCR基本都能识别,但涉及到比较“不规范”的字体(如雅艺、行楷等),这位兄台就有点无能为力了,特别是繁体字,这家伙简直就是在胡画。估计是我们只引用的简体中文和英文有关。
六、补充:
这个程序的框架基本上已经完成了,为了更好的使用,我们还可以增加其它功能。如添加一个复制按钮,点击后就可以把识别的文本直接复制到剪贴板,还可以增加一个状态栏,在其中反馈识别的进度等等。这些就让其它朋友来做吧。
另外==【注意】==一点:测试过程中发现图片文件名中不能有汉字和空格,只能为英文和数字,否则系统就会提示没有找到文件名之类的。具体原因我没有找到,大家分析吧。
更多推荐
所有评论(0)