Python使用EasyOCR做一个对多图的连续扫描识别的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基本都能识别,但涉及到比较“不规范”的字体(如雅艺、行楷等),这位兄台就有点无能为力了,特别是繁体字,这家伙简直就是在胡画。估计是我们只引用的简体中文和英文有关。

六、补充:

这个程序的框架基本上已经完成了,为了更好的使用,我们还可以增加其它功能。如添加一个复制按钮,点击后就可以把识别的文本直接复制到剪贴板,还可以增加一个状态栏,在其中反馈识别的进度等等。这些就让其它朋友来做吧。

另外==【注意】==一点:测试过程中发现图片文件名中不能有汉字和空格,只能为英文和数字,否则系统就会提示没有找到文件名之类的。具体原因我没有找到,大家分析吧。

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值